Skip to main content
Go back

· 2 min read
Ewerton Lopes

header

Introdução

No geral, os contratos factory são uma ferramenta útil para os desenvolvedores criarem e gerenciarem várias instâncias de contratos inteligentes de maneira mais eficiente e simplificada, fornecendo um bloco de construção para a criação de aplicativos descentralizados complexos no ecossistema Web3.

Ele permite a implantação de muitos contratos semelhantes com uma única transação, reduzindo o custo e a complexidade da implantação de vários contratos individualmente. A palavra-chave "new" é um elemento-chave no padrão de fábrica no Solidity e desempenha um papel significativo na implantação e gerenciamento de contratos Factory.

Neste video mostraremos como você pode realizar um deploy re um smart contract utilizando Factory na rede Alfajores (rede de teste) da Celo.

Pré-requisitos

Para este tutorial não é necessário conhecimento prévio. As ferramentas utilizadas serão:

  • Remix: Remix é uma Integrated Development Environment (IDE) para desenvolvimento de smart contracts na blockchain Ethereum. Ele fornece uma plataforma fácil de usar para escrever, testar e depurar smart contracts escritos na linguagem Solidity. Além disso, a IDE Remix também fornece ferramentas para simular e executar smart contracts em diferentes ambientes de rede, incluindo a rede principal Ethereum e redes de teste. Ele é uma ferramenta popular entre desenvolvedores de smart contracts, pois oferece recursos avançados para facilitar o desenvolvimento e teste de contratos inteligentes.
  • Faucet: Faucet é utilizada para adicionar fundos a sua conta de teste na rede Alfajores.
  • Alfajores: É a rede de teste da Celo que utilizaremos para demonstração a implantação de um contrato inteligente e também realizar transações dos ativos.

Requisitos

  • Criar duas contas na MetaMask usando a rede Alfajores da Celo

Tutorial

Confira no video como construir e realizar deploy de contratos Factory no blockchain Celo.

Como construir e realizar deploy de contratos Factory no blockchain Celo

Conclusão

Parabéns! Você concluiu o tutorial e agora sabe como criar um contracto Factory no blockchain da Celo 🎉 .

Próximos passos

Como próximos passos sugiro a você consultar outros videos.

Além disso, convido você a ver nossos próximos conteúdos em português.

Sobre o Autor

Eu sou um empreendedor serial, founder da Guizo Studios e sempre disponível para ajudar o ecossistema Celo.

LinkedIn

Go back

· 11 min read
Mayowa Julius Ogungbola

Introduction

Non-fungible tokens (NFTs) have gained significant popularity in recent years due to their ability to represent unique digital assets such as artworks, collectibles, and even virtual real estate. One specific type of NFT, called ERC1155, allows for the creation and management of both fungible tokens within a single smart contract.

Minting your own NFTs on Celo is a relatively straightforward process. this tutorial will provide the necessary steps to successfully mint your ERC115 NFT on the Celo Blockchain network, with tips and best practices for successful minting, using the Remix IDE.

Additionally, this tutorial will provide a summary of the advantages of using the Celo blockchain to mint ERC1155 NFTs. And helps you demystify the process and provides the tools and resources necessary for successful NFT minting.

Prerequisites

Remix: You should be familiar with working on the Remix IDE. This tutorial will make use of the Remix IDE to create, deploy and mint your ERC token on the Celo Blockchain.

OpenZeppelin: For creating your ERC1155 Token standard contract, you’ll make use of an already created and secured contract on the openzeppelin library available remotely on the internet.

This tutorial requires you to have a solid foundation knowledge of basic web3 concepts like NFTs, openzeppelin, smart contracts, solidity code, etc.

Note: Although this tutorial will cover creating and deployment of your ERC contract, it will not include an in-depth explanation of how to create and deploy your ERC115 contract.

Requirements​

To follow this tutorial you should have the following:

  • Celo Wallet Extension: This tutorial will require you to have an account on an installed celo wallet extension, or if you’re using another wallet like metamask, you should have the celo alfajores network added. Here is a link to guide you on how to add the alfajores testnet to your custom wallet.

  • Faucets: You should also have your wallet funded with Celo test funds. Here is a link to request celo faucets.

  • Node & node package management npm or yarn: This tutorial will require you to use a preinstalled node package manager, yarn. You should also know about working with any package manager: npm or yarn.

  • IPFS: Although this tutorial will not cover uploading your NFT images on IPFS, you should also be familiar with the concept of uploading on IPFS, and how to upload files on IPFS.

Note: IPFS (InterPlanetary File System) is a distributed file system that allows users to store and access content from anywhere in the world. It is built on the same principles as the Ethereum blockchain, making it the ideal choice for hosting and delivering content on the blockchain.

The ERC115 Token Standard

Before getting started with writing code and minting your ERC115 token, here is a quick reminder on what the ERC1155 token is and how it is usually minted.

The ERC115 Token Standard is one of the most popular Ethereum-based token standards, that is used for creating, minting, storing, and transferring Non-Fungible Tokens (NFTs).

It is an extension of the ERC20 Token Standard and allows users to create unique tokens with different attributes, such as name, symbol, and metadata. And a powerful tool for developers to easily create and manage digital assets on a blockchain network.

Tokens created with the ERC1155 Token Standard are also interoperable with other ERC20 tokens, for the creation of multiple asset types, such as collectibles, game items, digital artwork, and more. To mint a new token, developers need to call the ERC1155 contract and pass in the token's contract address, an array of token IDs, and an array of tokenURI strings. The tokenURI string is used to identify each token and can contain information such as a token’s title, description, or image.

The following code example shows the functions to mint a new ERC1155 token from a smart contract on a blockchain network. From openzeppelin’s standard ERC1155 contract:

function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
require(to != address(0), "ERC1155: mint to the zero address");

address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);

_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

_balances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);

_afterTokenTransfer(operator, address(0), to, ids, amounts, data);

_doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
}

Uploading the NFT image on IPFS

To get started with minting your NFT on the Celo blockchain. First, you need to have your image uploaded on IPFS, although this tutorial will not cover how to upload your pictures, here is a video reference to learn how to upload your NFT images to IPFS using NFT UP, Since you will need an already uploaded NFT image for this tutorial, you can use the link to an NFT folder with the images PHENZIC000 and PHENZIC001, uploaded on IPFS.

Minting your ERC1155 Token Standard

Remix is an open-source web IDE that simplifies the process of creating and deploying smart contracts to a blockchain network. It offers a simple graphical user interface that enables you to write and deploy Ethereum contracts quickly and easily. To get started with minting and interacting with your tokens, you’ll need to create a basic template for your smart contract on Remix, hence the following steps:

  1. First, head over to the Remix IDE using this link,
  2. You will need to download the Celo plugin on Remix, for interacting, compiling, and deploying on the Celo blockchain. Following the images and steps below.
  • a. Click on the plug-like icon on the left bottom part of the screen.

  • b. Enter Celo into the search bar to search for the Celo plugin.

  • c. Click Activate to add the Celo plugin to the left plain, you will notice the Celo icon has been added to the plugins on the left, click on the Celo Icon.

plugin_manager

  1. Next, create a new file under the contracts directory, and name the file MyToken, where you will have your smart contract written.

files

  1. Copy and paste the following code below into your MyToken contract:
  • a. The code below simply initializes the contract by importing the standard ERC1155 token contract from openzeppelin into your contract, including all the functionalities of a standard ERC1155 token:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

Note: OpenZeppelin is an open-source framework for developing secure, reliable, and upgradable smart contracts on the blockchain. It provides a large library of reusable, secure, and well-tested components that can be used to quickly develop and deploy applications on the Ethereum blockchain.

Copy and paste the code above inside your MyToken.sol contract file.

  • b. Next, the function below creates a new contract as a sub-contract of the standard openzeppelin ERC1155 contract and also initializes two constants for the NFTs you will be creating.
contract MyToken is ERC1155 {
uint256 public constant PHENZIC000 = 0;
uint256 public constant PHENZIC001 = 1;
}

Copy and add the code above to your MyToken contract file.

  • c. The Next function is the constructor function that takes in the link to your NFT images uploaded on IPFS and calls the _mints function to mint your NFTs.
    constructor() ERC1155("https://bafybeiaqqz4unoubpu2oz2rsgowh3irdqnpcqjoyspzwrepnrwql7rgvy4.ipfs.nftstorage.link/"){
_mint(msg.sender, PHENZIC000, 200, "");
_mint(msg.sender, PHENZIC001, 100, "");
}

Copy and add the code above to your token contract.

  • d. Finally the last function uri take in unit256 digit 0 or 1. And returns the direct link to any of the NFT's locations on IPFS, either the first no or the second.
    function uri(uint256 _tokenId) override public pure returns (string memory) {
return string(abi.encodePacked("https://bafybeiaqqz4unoubpu2oz2rsgowh3irdqnpcqjoyspzwrepnrwql7rgvy4.ipfs.nftstorage.link/PHENZIC00",Strings.toString(_tokenId),".jpeg")
);
}

Copy and add the code below inside your token contract.

  • e. Finally, your complete contract should look exactly like the one code below, you can copy and replace your entire code with this for uniformity's sake.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract MyToken is ERC1155 {
uint256 public constant PHENZIC000 = 0;
uint256 public constant PHENZIC001 = 1;

constructor() public ERC1155("https://bafybeiaqqz4unoubpu2oz2rsgowh3irdqnpcqjoyspzwrepnrwql7rgvy4.ipfs.nftstorage.link/"){
_mint(msg.sender, PHENZIC000, 200, "");
_mint(msg.sender, PHENZIC001, 100, "");
}

function uri(uint256 _tokenId) override public pure returns (string memory) {
return string(abi.encodePacked("https://bafybeiaqqz4unoubpu2oz2rsgowh3irdqnpcqjoyspzwrepnrwql7rgvy4.ipfs.nftstorage.link/PHENZIC00",Strings.toString(_tokenId),".jpeg")
);
}
}

  1. Next click on the Celo plug-in icon you activated earlier, to compile and deploy your contract on celo.

  2. Now, click on the connect button attached to the first input bar on the side plain, to connect to your wallet, you will notice it automatically returns the amount of native token on your alfajores wallet and also tells you the network you are on currently.

Note: Also ensure you are current on your celo alfajores testnet account on any wallet of your choice, and the account should already be funded with Celo testnet native token.

  1. Next, Click on the compile button, and Remix should compile the contract without any errors.

compile_token

  1. Now click on the deploy button below, and your wallet will pop up on the right side of your screen for you to sign the transaction.

deploy_myToken

  1. Click on confirm button, and you should have your contract already deployed to the celo alfajores testnet with the address of your contract on the display tab attached to the deploy button.

confirm_transaction

Note: The NFTs minted will be signed to the address of the person that deployed the contract and called the function.

  1. Now, that you have deployed your token contract with the minting function being called inside the contract’s constructor function. Your NFTs have automatically been minted to your address.

Interacting with the Deployed ERC1155 Token

Now that you have your newly minted ERC1155 token deployed and minted on Celo alfajores signed with your address. You can run some checks to interact with your token on the blockchain.

  • To check the number (balance) of NFT currently available on your wallet address:

a. Click on the function call balanceof at the bottom left side of your screen.

b. You will notice two drop-down input tabs to add your connected wallet address and the token Id of the NFT you want to view its balance, Copy your connected wallet address and past it in the first field, and input 0 in the second tab to view the balance of the NFT.

c. Click Call to view the amount of the PHENZIC000 token available in your address.

remix_balance_Of

  • Assuming you have a Gaming application where you welcome each new player by gifting them with the PHENZIC000 token and gifting the old-timer player with certain levels attained in the game with the PHENZIC001 token. You can have this done automatically on your Dapp but in this case, you can equally send these tokens by calling the safeTransferFrom function.

a. First, click on the safeTransferFrom function call like in the image below to add the required parameters to call the functions.

b. Copy and add your connected wallet address as the first input for the variable from.

c. Next, add the wallet address you want to send the NFT to, any remote alfajores address will work fine.

d. Enter the id of the token you want to send 0 or 1, and the amount you want to send for this example you can use 0 for id, 50 for the amount, and use 0x00 for the data input.

e. Click transact, and to check the balance of the remaining token.

f. Head back to the balanceof add your connected wallet address and 0 or the id of the token you sent from, and click call. You will notice the reduction in the token amount.

functions

  • The uri function call overrides simple taken in a token_id, and returns the link to either the first minted NFT or the second minter NFT on IPFS, depending on the token_id you input.

Conclusion

Minting your ERC1155 NFT on the Celo blockchain is a relatively straightforward process, but it does require some technical knowledge and setup. By following the steps outlined in this tutorial, you can successfully mint your ERC115 NFT and begin it in the Celo ecosystem.

You would have used the Celo plugin on the Remix IDE to interact with the Celo blockchain and, understand other concepts like how the IPSF works and how to mint your token contract using Remix.

About the Author

Mayowa Julius Ogungbola

is a Software Engineer and Technical writer always open to working on new ideas. I enjoy working on GitHub and you can also connect with me on LinkedIn.

Next Steps

Here are some other NFT-related tutorial articles you might be interested in:

References​

Here are links to some video tutorials you might be interested in following along with while reading this tutorial:

Go back

· 13 min read

header

Introduction​

In this tutorial, we will be creating an escrow NFT platform on the Celo network using Eth-Brownie Python. Escrow is a financial arrangement where a third party holds and regulates the transfer of funds or assets between two other parties. In this case, the third party is the smart contract. The two parties are the buyer and the seller. The escrow NFT platform will work like this, the seller will lock NFT in smart contract and the buyer will be able to buy the NFT by paying the price set by the seller. The smart contract will hold the NFT until the buyer pays the price. If the buyer pays the price, the NFT will be transferred to the buyer. If the buyer fails to pay the price, the NFT will be returned to the seller. In this tutorial, we will be using the Celo network, but the same concept can be applied to other evm-compatible blockchain networks.

Prerequisites

These tutorials assume that you have some basic knowledge of solidity and python.

Requirements

This is a list of what we’ll cover 🗒

  • Step 1: Project setup
  • Step 2: Write project code
  • Step 3: Configure deployment settings
  • Step 4: Deploy your Contract
  • Step 5: Integration with frontend

Step 1: Project setup

First, we will create a new directory for our project. Open your terminal and run the following command to create a new directory called escrow-nft and change directory to it.

mkdir escrow-nft && cd escrow-nft

next, we will install eth-brownie, python-dotenv, and ganache-cli. Run the following command to install them.

# Install eth-brownie and python-dotenv
pip3 install eth-brownie python-dotenv

# Install ganache
npm install -g ganache

Now, after installing all the required packages, we need to initialize our project. Run the following command to initialize our project.

brownie init

Here's what a successful initialization looks like: init

Next, after initializing our project, we will create two files, brownie-config.yaml and .env in root directory. brownie-config.yaml is a configuration file for brownie. It contains the default settings for our project. We will use this file to configure our project settings. .env is a file that contains environment variables. We will use this file to store our mnemonic phrase. We will use this mnemonic phrase to deploy our contract to the Celo network.

brownie-config.yaml file

reports:
exclude_contracts:
- SafeMath
depedencies:
- OpenZeppelin/openzeppelin-contracts@4.8.0
compiler:
solc:
version: 0.8.15
optimizer:
enabled: true
runs: 200
remappings:
- "@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.8.0"
networks:
default: celo-alfajores
console:
show_colors: true
color_style: monokai
auto_suggest: true
completions: true
editing_mode: emacs
dotenv: .env
wallets:
from_mnemonic: ${MNEMONIC}

.env file

MNEMONIC="your mnemonic phrase"

if you want to read more about brownie-config.yaml file, you can read it here.

lastly for this step, we will add Celo network to our brownie project. Run the following command to add Celo network to our brownie project.

brownie networks add Celo celo-mainnet host=https://forno.celo.org chainid=42220 explorer=https://explorer.celo.org

brownie networks add Celo celo-alfajores host=https://alfajores-forno.celo-testnet.org chainid=44787 explorer=https://alfajores-blockscout.celo-testnet.org

You can check if the network has been added by running the following command.

brownie networks list

result if the network has been added successfully.

networks list

You can see that we have added two networks, celo-mainnet and celo-alfajores to our brownie network list.

Step 2: Write project code

In this step, we will write the code for our smart contract. Create a new file called escrowNFT.sol in the contracts directory. This is where we will write our smart contract code. Here is an look at what our smart contract code will look like.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract escrowNFT is Ownable {
using SafeMath for uint256;

// Declare state variables
uint256 public fee;
uint256 private escrowDigit = 16;
uint256 private modulus = 10**escrowDigit;

enum Status {
Pending,
Accepted,
Rejected,
Cancelled
}

struct Escrow {
uint256 tokenId;
uint256 paymentAmount;
uint256 deadline;
address tokenAddress;
address buyerAddress;
address sellerAddress;
Status status;
}

mapping(uint256 => Escrow) public escrow;

event NewEscrow(
uint256 txId,
uint256 tokenId,
uint256 paymentAmount,
address tokenAddress
);

event CancleEscrow(
uint256 txId,
uint256 tokenId,
uint256 paymentAmount,
address tokenAddress
);

event PayEscrow(
uint256 txId,
uint256 timestamp,
uint256 tokenId,
uint256 paymentAmount
);

event RejectEscrow(
uint256 txId,
uint256 tokenId,
uint256 paymentAmount,
address tokenAddress
);



modifier onlySeller(uint256 _txId) {
require(
msg.sender == escrow[_txId].sellerAddress,
"Only seller can call this function"
);
_;
}

modifier onlyBuyer(uint256 _txId) {
require(msg.sender == escrow[_txId].buyerAddress, "Only buyer can call this function");
_;
}

constructor(uint256 _fee) {
fee = _fee;
}

function updateFee(uint256 _fee) external onlyOwner {
fee = _fee;
}

function claimFee() external onlyOwner {
(bool status, ) = payable(msg.sender).call{value: address(this).balance}("");
require(status, "Transfer failed");
}

function generateTxId(
address _sellerAddress,
address _buyerAddress,
address _nftAddress,
bytes memory _secret
) external view returns (uint256 txId) {
txId =
uint256(
keccak256(
abi.encodePacked(
_sellerAddress,
_buyerAddress,
_nftAddress,
_secret
)
)
) %
modulus;
}

function createEscrow(
uint256 _txId,
uint256 _tokenId,
uint256 _paymentAmount,
address _tokenAddress,
address _buyerAddress
) external {
require(_paymentAmount > 0, "Payment amount must be greater than 0");
require(_tokenAddress != address(0), "Token address cannot be 0x0");
require(_buyerAddress != address(0), "Buyer address cannot be 0x0");
IERC721 nft = IERC721(_tokenAddress);
nft.transferFrom(msg.sender, address(this), _tokenId);
escrow[_txId] = Escrow(
_tokenId,
_paymentAmount,
block.timestamp + 1 days,
_tokenAddress,
_buyerAddress,
msg.sender,
Status.Pending
);
emit NewEscrow(_txId, _tokenId, _paymentAmount, _tokenAddress);
}

function cancleEscrow(uint256 _txId) external onlySeller(_txId) {
require(
block.timestamp > escrow[_txId].deadline,
"Deadline not reached"
);
require(
escrow[_txId].status == Status.Pending,
"Escrow is not in pending status"
);
IERC721 nft = IERC721(escrow[_txId].tokenAddress);
escrow[_txId].status = Status.Cancelled;
nft.transferFrom(address(this), msg.sender, escrow[_txId].tokenId);
emit CancleEscrow(
_txId,
escrow[_txId].tokenId,
escrow[_txId].paymentAmount,
escrow[_txId].tokenAddress
);
}

function payEscrow(uint256 _txId) external payable onlyBuyer(_txId) {
require(block.timestamp < escrow[_txId].deadline, "Deadline reached");
require(
escrow[_txId].status == Status.Pending,
"Escrow is not in pending status"
);
IERC721 nft = IERC721(escrow[_txId].tokenAddress);
uint256 amountAfterFee = _calculateFee(msg.value);
escrow[_txId].status = Status.Accepted;
(bool status, ) = payable(escrow[_txId].sellerAddress).call{value: amountAfterFee}("");
require(status, "Transfer failed");
nft.transferFrom(address(this), msg.sender, escrow[_txId].tokenId);
emit PayEscrow(
_txId,
block.timestamp,
escrow[_txId].tokenId,
escrow[_txId].paymentAmount
);
}

function rejectEscrow(uint256 _txId) external onlyBuyer(_txId) {
require(block.timestamp < escrow[_txId].deadline, "Deadline reached");
require(
escrow[_txId].status == Status.Pending,
"Escrow is not in pending status"
);
IERC721 nft = IERC721(escrow[_txId].tokenAddress);
escrow[_txId].status = Status.Rejected;
nft.transferFrom(address(this), escrow[_txId].sellerAddress, escrow[_txId].tokenId);
emit RejectEscrow(
_txId,
escrow[_txId].tokenId,
escrow[_txId].paymentAmount,
escrow[_txId].tokenAddress);
}

function _calculateFee(uint256 _paymentAmount) private view returns(uint256 amountAfterFee) {
uint256 feeAmount = _paymentAmount.mul(fee).div(100);
amountAfterFee = _paymentAmount.sub(feeAmount);
}
}

Let us analyze the code line by line.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

First line, we declare the SPDX license identifier of the relevant license for the contract. The second line, we declare the solidity version we are using.

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

We'll be using OpenZeppelin contracts for our project. Ownable contract will be used to make sure only the owner of the contract can call certain functions. IERC721 is the interface for ERC721 tokens. SafeMath is a library that provides mathematical functions and prevents integer overflow.

uint256 public fee;
uint256 private escrowDigit = 16;
uint256 private modulus = 10**escrowDigit;

The fee variable will be used to store the fee percentage. The escrowDigit variable will be used to generate a unique transaction ID. The modulus variable will be used to generate a unique transaction ID.

enum Status {
Pending,
Accepted,
Rejected,
Cancelled
}

Enum Status will be used to store the status of the escrow.

struct Escrow {
uint256 tokenId;
uint256 paymentAmount;
uint256 deadline;
address tokenAddress;
address buyerAddress;
address sellerAddress;
Status status;
}

Escrow struct will be used to store the details of the escrow.

mapping(uint256 => Escrow) public escrow;

The escrow mapping will be used to map the transaction ID to the Escrow struct.

event NewEscrow(
uint256 txId,
uint256 tokenId,
uint256 paymentAmount,
address tokenAddress
);

event CancleEscrow(
uint256 txId,
uint256 tokenId,
uint256 paymentAmount,
address tokenAddress
);

event PayEscrow(
uint256 txId,
uint256 timestamp,
uint256 tokenId,
uint256 paymentAmount
);

event RejectEscrow(
uint256 txId,
uint256 tokenId,
uint256 paymentAmount,
address tokenAddress
);

These events are emitted when a new escrow is created, an escrow is cancelled, an escrow is paid, and an escrow is rejected.

modifier onlySeller(uint256 _txId) {
require(
msg.sender == escrow[_txId].sellerAddress,
"Only seller can call this function"
);
_;
}

modifier onlyBuyer(uint256 _txId) {
require(msg.sender == escrow[_txId].buyerAddress, "Only buyer can call this function");
_;
}

These modifiers are used to make sure only the seller or the buyer can call certain functions.

function updateFee(uint256 _fee) external onlyOwner {
fee = _fee;
}

function claimFee() external onlyOwner {
(bool status, ) = payable(msg.sender).call{value: address(this).balance}("");
require(status, "Transfer failed");
}

The updateFee function is used to update the fee percentage. The claimFee function is used to claim the fee collected by the contract.

function generateTxId(
address _sellerAddress,
address _buyerAddress,
address _nftAddress,
bytes memory _secret
) external view returns (uint256 txId) {
txId =
uint256(
keccak256(
abi.encodePacked(
_sellerAddress,
_buyerAddress,
_nftAddress,
_secret
)
)
) %
modulus;
}

generateTxId function is used to generate a unique transaction ID to be used for the escrow. The transaction ID is generated using the seller address, buyer address, NFT address, and the secret. secret parameter is used to make sure the transaction ID is unique for each escrow.

function createEscrow(
uint256 _txId,
uint256 _tokenId,
uint256 _paymentAmount,
address _tokenAddress,
address _buyerAddress
) external {
require(_paymentAmount > 0, "Payment amount must be greater than 0");
require(_tokenAddress != address(0), "Token address cannot be 0x0");
require(_buyerAddress != address(0), "Buyer address cannot be 0x0");
IERC721 nft = IERC721(_tokenAddress);
nft.transferFrom(msg.sender, address(this), _tokenId);
escrow[_txId] = Escrow(
_tokenId,
_paymentAmount,
block.timestamp + 1 days,
_tokenAddress,
_buyerAddress,
msg.sender,
Status.Pending
);
emit NewEscrow(_txId, _tokenId, _paymentAmount, _tokenAddress);
}

createEscrow is used to create a new escrow. The function takes the transaction ID, token ID, payment amount, token address, and buyer address as parameters. The function first checks if the payment amount is greater than 0, and check token, buyer address are not 0x0. Then it transfers the NFT from the seller to the contract. Then it creates a new escrow in the escrow mapping and emits the NewEscrow event.

function cancleEscrow(uint256 _txId) external onlySeller(_txId) {
require(
block.timestamp > escrow[_txId].deadline,
"Deadline not reached"
);
require(
escrow[_txId].status == Status.Pending,
"Escrow is not in pending status"
);
IERC721 nft = IERC721(escrow[_txId].tokenAddress);
escrow[_txId].status = Status.Cancelled;
nft.transferFrom(address(this), msg.sender, escrow[_txId].tokenId);
emit CancleEscrow(
_txId,
escrow[_txId].tokenId,
escrow[_txId].paymentAmount,
escrow[_txId].tokenAddress
);
}

cancleEscrow function is used to cancel an escrow. The function takes the transaction ID as a parameter. First, function will check if the deadline is reached and the escrow status is pending. If both conditions are true, the function will transfer the NFT back to the seller and emit the CancleEscrow event.

function payEscrow(uint256 _txId) external payable onlyBuyer(_txId) {
require(block.timestamp < escrow[_txId].deadline, "Deadline reached");
require(
escrow[_txId].status == Status.Pending,
"Escrow is not in pending status"
);
IERC721 nft = IERC721(escrow[_txId].tokenAddress);
uint256 amountAfterFee = _calculateFee(msg.value);
escrow[_txId].status = Status.Accepted;
(bool status, ) = payable(escrow[_txId].sellerAddress).call{value: amountAfterFee}("");
require(status, "Transfer failed");
nft.transferFrom(address(this), msg.sender, escrow[_txId].tokenId);
emit PayEscrow(
_txId,
block.timestamp,
escrow[_txId].tokenId,
escrow[_txId].paymentAmount
);
}

payEscrow function is used to pay an escrow. The function takes the transaction ID as a parameter. First, function will check if the deadline is not reached and the escrow status is pending and buyer only sends the payment amount equal to the payment amount in the escrow. If all conditions are true, the function will transfer the NFT to the buyer and pay the seller the payment amount minus the fee. Then it emits the PayEscrow event.

function rejectEscrow(uint256 _txId) external onlyBuyer(_txId) {
require(block.timestamp < escrow[_txId].deadline, "Deadline reached");
require(
escrow[_txId].status == Status.Pending,
"Escrow is not in pending status"
);
IERC721 nft = IERC721(escrow[_txId].tokenAddress);
escrow[_txId].status = Status.Rejected;
nft.transferFrom(address(this), escrow[_txId].sellerAddress, escrow[_txId].tokenId);
emit RejectEscrow(
_txId,
escrow[_txId].tokenId,
escrow[_txId].paymentAmount,
escrow[_txId].tokenAddress);
}

rejectEscrow function is used to reject an escrow. The function takes the transaction ID as a parameter. First, function will check if the deadline is not reached and the escrow status is pending. If both conditions are true, the function will transfer the NFT back to the seller and emit the RejectEscrow event.

function _calculateFee(uint256 _paymentAmount) private view returns(uint256 amountAfterFee) {
uint256 feeAmount = _paymentAmount.mul(fee).div(100);
amountAfterFee = _paymentAmount.sub(feeAmount);
}

_calculateFee private function is used to calculate the fee amount. The function takes the payment amount as a parameter and returns the amount after fee.

Step 4: Deploy your Contract

Now that we have written our smart contract, we need to deploy it to the blockchain. First, we need to compile our smart contract, and then we will deploy it to the blockchain. Use the following command to compile the smart contract.

brownie compile

result if the compilation is successful brownie compile

Now, we need to deploy our smart contract to the blockchain. First create a new file scripts/deploy.py and add the following code to it.

from brownie import accounts, config, escrowNFT

def main():
# Get the account to use
account = accounts.from_mnemonic(config["wallets"]["from_mnemonic"])

# Deploy the contract
contract = escrowNFT.deploy(2, {"from": account})

# Wait for the transaction to be mined
contract.tx.wait(3)

print("Contract deployed to:", contract.address)

main function is used to deploy the contract. First, we get the account to use. Then we deploy the contract and wait for the transaction to be mined. Finally, we print the contract address. Now, we need to deploy the contract. Use the following command to deploy the contract.

# Deploy the contract to the Celo Alfajores testnet
brownie run scripts/main.py --network celo-alfajores

# Deploy the contract to the Celo Mainnet
brownie run scripts/main.py --network celo-mainnet

result if the deployment is successful brownie run

Step 5: Integration with frontend

Now that we have deployed our smart contract to the blockchain, we need to integrate it with our frontend. You can clone the frontend we have created for this tutorial. Use the following command to clone the frontend.

# Clone the frontend
git clone https://github.com/yafiabiyyu/CeloSageFE.git

# Go to the frontend directory
cd CeloSageFE

# Install the dependencies
npm install --save

After cloning the frontend, we need to update the src/utils/contract.tsx, You need to update the contract address and ABI. You can get the contract address from the deployment result and you can get the ABI from the brownie project folder build/contracts/escrowNFT.json.

after updating the contract address and ABI, You can run the frontend using the following command.

npm start

result if the frontend is running successfully frontend result

Conclusion

In this tutorial, we have learned how to create an escrow smart contract using Solidity and Brownie. We have also learned how to deploy the smart contract to the Celo blockchain.

Next Steps

For your next steps, if you a python developer and want to learn how to create a smart contract using python, you can check about vyper. Vyper is a python-based smart contract programming language. You can check the Vyper documentation to learn more about Vyper.

About the Author

I am a blockchain and crypto enthusiast. I am also a software engineer. I love to learn new things and share my knowledge with others. You can find me on GitHub and LinkedIn.

Go back

· 11 min read
Mayowa Julius Ogungbola

header

Introduction

The Celo blockchain is a proficient, fast, and lightweight platform that supports the building of innovative, complex, and client-designed mobile applications. In simple terms, it is a network that allows the development of decentralized and inventive mobile and web applications.

One of the core features of how the Celo blockchain work falls on the concept of multiple revolutionary solutions, one of which is called Proof of Stake (PoS). On completing this article, you’ll have a solid idea of the Concept of Celo’s protocols, what PoS is and how it makes Celo an efficient platform for creating indigenous, decentralized solutions.

You’ll also have all the basic information you’ll need to get started with building on the Celo blockchain.

Prerequisites

Throughout this article, you are not expected to have any prior in-depth knowledge of any technology or intricate detail about the web3 space. If you’re reading this tutorial, it means you want to know more about what a PoS is and how the Celo Blockchain integrates this verification system.

What is Consensus Mechanism

A Consensus Mechanism is a method of authentication adopted by blockchain platforms to ensure transactions are in sync and agree on which transaction is valid before adding the transaction to the blockchain.

Amongst others, one of the proven efficient and effective means of reaching consensus on the blockchain is using the Proof of Stack PoS consensus mechanism. Which is why the Celo blockchain uses it.

When a transaction is created on the Celo blockchain before it is added to the blockchain ledger it first needs to be validated by the chain’s miners, thus the need for consensus.

What is Proof of Stack (PoS)

Proof of Stack is a type of consensus mechanism that adopts the idea of staking coins to earn its node runners the right to validate a transaction before adding it to the blockchain ledger.

When a transaction occurs on a blockchain platform like Celo, there is a need to first authenticate and validate the transaction before adding the transaction to the blockchain permanently. These tasks are usually carried out by the blockchain’s miners and node runners on the Celo blockchain.

Note: Node runners or validators are individuals or companies running full blockchain network nodes. They provide the backbone of the blockchain network by providing the infrastructure that allows the network to process transactions and maintain a distributed ledger. Node runners are rewarded for their services with tokens or coins from the network.

Looking back to the technology system before the web3 revolutionary breakthrough, verifying transactions would require a centralized or automated entity that was prone to either time consumption for financial and data forgery for non-monetary transactions and a lot more.

The need for a system like the PoS for the validation of transactions, therefore came as an innovative technological breakthrough.

In a PoS system, the chances of forgery or manipulation of transactions and data would be easily spotted and penalized. The PoS means of consensus is one of the important features that make the Celo Blockchain a secure and rewarding platform for developers to build on and validators to manage and get rewarded for.

The PoS algorithm also allows its validators and node runners to carry out validation while maintaining a low computational cost and manual effort.

Other types of Consensus Mechanism

Just like the PoS is used by the Celo blockchain as a means of reaching Consensus in approving all transactions, there are also other consensus mechanisms adopted by other blockchains some of which are;

  1. Proof of Work (PoW): This type of consensus algorithm requires its miners to consume a massive amount of computational power to solve the complex cryptographic puzzle that defines each transaction. Although this algorithm is effective and adopted by popular blockchain platforms, it is not quite efficient.

  2. Delegated Proof of Stake (DPoS): This type of consensus algorithm is similar to the PoS algorithm. But unlike the PoS algorithm, a group of miners or node runners is tasked with achieving a distributed consensus. It serves more like a voting system where miners decide who should be responsible for validating a transaction.

  3. Proof Of Importance (PoI): This algorithm is also very similar to the Proof of Stake algorithm, In the mechanism rewards are given to effective network users. The algorithm allows a hierarchy of importance based on individuals who have made more contributions to the system and gives them the right to validate transactions on the platform.

  4. Proof of Activity (PoA): This algorithm is a hybrid of the Proof of Work (PoW) and the Proof of Stake (PoS) algorithms, that allow validators to stake coins as collateral, and also validate blocks using computational decryption and other resources.

  5. Proof of Captivity (PoC): Thissystem of consensus neither requires a miner si solve cryptographic puzzles like the PoW or stake coins like the PoS, but rather to prove that they have a certain amount of hard drive space to contribute to storing plots of cryptographic hashes for the blockchain.

  6. Proof of Authority (PoA): This algorithm simply pre-selects and authorizes validator nodes to validate a newly added block.

  7. Proof of Elapsed Time (PoET): This system of consensus was designed to simply select a leader from a pool of miners and validators and assigns the task of validating the next node.

These are a few other important consensus algorithms available and adopted by other blockchain platforms.

Other Core Feature Of the Celo Blockchain

  • Scalability: The Celo blockchain was built to handle a large number of users and transactions without slowing down or becoming unresponsive. Celo is a layer-1 blockchain solution that helps to scale up blockchain technology to handle a high throughput of transactions. It does this by allowing transactions to be validated off-chain and then quickly grouping them into batches for faster processing on-chain. This helps reduce overall network congestion and improves scalability.

  • Security: Transactions on the Celo Blockchain go through a set of measures taken to protect the data and transactions stored on the blockchain from tampering or unauthorized access. This includes encryption, secure protocols, and validation of transactions. Additionally, Celo's consensus protocol is designed to ensure the safety and integrity of the blockchain by maintaining a decentralized network of validators who are incentivized to keep the blockchain secure.

  • Speed: The Celo blockchain is designed to offer a high level of speed, and enables users to quickly and securely transfer value across the network in a matter of seconds. It also utilizes a sharding technology called Celo Fast Finality (CFF) that allows the network to split its transactions into multiple shards to process thousands of transactions per second. It utilizes a mechanism called instant finality that also allows the network to commit transactions.

Building on Celo

As a developer looking to build fast, secure, scalable, and innovative ideas, building on Celo is an exciting opportunity for you to create applications and services that leverage the Celo platform. Celo provides a secure, open-source platform for developers to create distributed applications and services that connect people and organizations in meaningful ways. With Celo, developers can create applications that bring new possibilities to the global economy, from unlocking financial inclusion to providing access to new markets.

Wallets

The Celo wallet enables its users to quickly and securely send and receive payments, store digital assets, and access various financial services. It allows users to view their account balances and track their transactions.

The wallet also provides developers with the tools they require to interact with the Celo Blockchain, like signing transactions, deploying and testing contracts, calling and testing functions, etc. Additionally, the Celo wallet offers enhanced security features to ensure the safety of users’ digital assets. With its suite of features, the Celo wallet is a powerful tool for building on the Celo blockchain. Indirectly you can also interact with the celo blockchain by adding the celo network to other wallets like metamask, etc. More wallet-related information and why you need one can be found here.

Smart Contract

The Celo blockchain also allows technological transactions like compiling, testing, debugging, deploying, and calling contracts on the network, which gives you the ability to create, a decentralized application and interact with a decentralized codebase on the blockchain.

Here, you will find more tutorials on creating, verifying, and deploying smart contracts, writing contract tests, and making contract call on the Celo blockchain using, Hardhat, Truffle, foundry, Remix, etc.

Connecting to Celo

When connecting to the Celo alfajores or maiNet for deploying or interacting with the network, you’re advised to use the Celo configuration file below in place of the code in your .config file.

require("@nomiclabs/hardhat-waffle");
require("dotenv").config({ path: ".env" });
require("hardhat-deploy");

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

// Prints the Celo accounts associated with the mnemonic in .env
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();

for (const account of accounts) {
console.log(account.address);
}
});

/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
defaultNetwork: "alfajores",
networks: {
localhost: {
url: "http://127.0.0.1:7545",
},
alfajores: {
gasPrice: 1500000000,
gas: 4100000,
url: "https://alfajores-forno.celo-testnet.org",
accounts: {
mnemonic: process.env.MNEMONIC,
path: "m/44'/52752'/0'/0",
},
//chainId: 44787
},
celo: {
url: "https://forno.celo.org",
accounts: {
mnemonic: process.env.MNEMONIC,
path: "m/44'/52752'/0'/0",
},
chainId: 42220,
},
},
solidity: "0.8.10",
};

Here is a link to the code sample above, you will also require your funded wallet’s Mnemonic phrase in an encrypted file. To know more about interacting with the Celo blockchain here are some tutorials you can read on.

Creating Dapps on the Celo blockchain

The Celo blockchain provides the infrastructure for decentralized applications (DApps) to act as a bridge between users and their data privacy. The increasing number of dApps that utilize the Celo blockchain validates its usefulness in the blockchain ecosystem.

Creating the Celo Blockchain is a revolutionary way to use blockchain technology to build secure and reliable mobile and web applications. With the Celo platform, developers can easily create decentralized applications (DApps) and smart contracts that enable users to transfer value, store information, and more. This platform is designed to be user-friendly and secure, making it an attractive option for developers looking to build the next generation of applications.

Redeploying to Celo

Redeploying your Decentralized Application (Dapp) to Celo is a necessary process that can enable you to take advantage of the Celo network’s advantages, including scalability and security. The Celo platform enables developers to create and deploy their Dapps quickly and easily, and redeploying is an important part of this process.

Redeploying your Dapp to Celo requires a Celo account, which you can create through their website or the Celo mobile app. Once you have an account, you must create a Dapp and deploy it to the Celo blockchain. This process involves setting up a smart contract, enabling the Celo network to run your Dapp. You then need to write the code for your Dapp and deploy it onto the Celo blockchain. Once your Dapp is deployed, it will be available to anyone on the Celo platform. Here you can find more information on Redeploying your Dapp to the Celo network.

Redeploying on the Celo network provides the following benefits to your decentralized application.

  1. Scalability: The Celo blockchain can scale to millions of users, with each user able to transact in a matter of seconds. This makes it ideal for large-scale applications that handle large volumes of transactions.

  2. Low Cost: Celo has committed to providing users with low-cost transactions, allowing developers to lower the cost of running their Dapps and making them more attractive to users.

  3. Security: Celo utilizes advanced cryptography to provide a secure and reliable platform for developers and users.

  4. Ease of Use: Celo has a user-friendly interface that makes it easy for developers to deploy their Dapps and for users to use them.

  5. Open Source: Celo is an open-source platform that allows developers to access and customize the code to their needs and requirements.

  6. Community Support: Celo has a vibrant and supportive community of developers and users who are always willing to help out and provide assistance.

Conclusion

Now that you have completed this tutorial, you understand the concept of one of the core concepts that make up the Celo blockchain. And you now have everything you need to start building and interacting with the celo blockchain.

Next Steps

Now that you’ve successfully grasped the lesson in this tutorial, you can also read on the following topics to help you get started on building real-world solutions and other development on Celo.

You can also consider contributing to the Celo network as a developer or as a technical writer (Celo Sage).

About the Author​

Mayowa Julius Ogungbola

A Software Engineer and technical writer who is always open to working on new ideas. I enjoy working on GitHub, and you can also find out what I tweet about and connect with me on LinkedIn

Go back

· 14 min read
Mayowa Julius Ogungbola

header

Introduction

When creating decentralized applications that leverage smart contracts, it is important to ensure that there are little or no vulnerabilities to prevent an attacker from compromising your application.

Unit testing helps you ensure that all functionalities in your contract are working as expected, and development environments like Truffle give you the same tools to help you write proficient tests for your contracts before final deployment.

In this tutorial, you’ll create an exemplary contract and learn how to write and run unit tests for your contract using the truffle development environment.

Prerequisites

Throughout this tutorial you’ll need to have worked with or have a basic knowledge of the following:

  • Truffle Suite: Truffle suite is a Development Environment that acts as a pipeline for interacting with the EVM and also provides essential features and valuable libraries for testing Ethereum smart contracts and makes it easy to interact with the blockchain.
  • Solidity: Solidity is a high-level programming language used for creating smart contracts.
  • Javascript: This tutorial will make use of Javascript, therefore you should be familiar with basic Javascript coding and algorithms.

Requirements

This tutorial also aspects that you have the following already installed or available:

  • Node & node package management npm or yarn: This tutorial will require you to use a preinstalled node package manager. You should also know about working with any package manager: npm or yarn.

Installing and setting up Truffle suite

To install the truffle suite using your terminal. Create a workspace, head over to the directory on your terminal, and run the command npm install -g truffle.

Now, run the command npx truffle init to fire up the development environment. You’ll notice a new file structure appears in your file explorer, something like the image below:

truffle_init

Running a Contract Test Simulation

To understand how unit testing works using the Truffle suite create a demo directory, different from your main directory, and run the command npx truffle unbox metacoin. The result of the successful run of the code should look like the image below.

creating_metacoin

The command starts up a demo project called <metacoin> including two contract files MetaCoin.sol and ConvertLib.sol in the contract directory and also has two testing files TestMetaCoin.sol and metacoin.js file in the test directory. For running unit tests on the metacoin contracts.

Now run the command npx truffle test and the result of the unit test should look exactly like the image below.

demo_testing

Truffle first compiles the contract, runs all the unit test in the test script, and returns the result of all the tests. The image above shows the result when it passes all the unit tests.

Creating the Smart Contract

Each contract test made is composed explicitly to test a specific contract, meaning if you have four different contract files in an application, your application should likewise have four test scripts for testing each contract. In the following steps, you'll write a simple sample contract which you'll, later be writing a for.

Note: If you’re new to solidity and creating smart contracts, check out this tutorial to get started and understand solidity code. The tutorial above also has a couple of functions that will help you learn how to write solidity code.

  1. Head back to the initial development environment directory you created; inside the contract folder, create a new file, Sample.sol. This will be the smart contract you’ll be writing unit tests for.

  2. The Sample.sol contract will have the following functionalities:

  • a. First, the contract is created, and the variables name, and age are also created and by default, have no value.
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

contract Sample {
string public name;
address public owner;


}
  • b. Next, the contract’s constructor function assigns the address of the deployer of the contract to the variable owner and assigns the string "deployer" to the name variable.

       constructor() {
    owner = msg.sender;
    name = "deployer";
    }
  • c. The next function rename accepts a string value as argument and assigns it to the variable name.

  
function rename(string memory _name) public {
name = _name;
}
  • d. The next function describe simply return the current values of the global variable, name.
  
function describe() public view returns (string memory) {
return (name);
}
  • e. Next is a modifier function ownerOnly that only allows the contract owner to call its parent function when added to any function.
    modifier ownerOnly() {
require(
msg.sender == owner,
"this function requires the owner of the contract to run"
);
_;
}
  • f. The following function changeOwner uses the previously created ownerOnly modifier to only allow the owner of the contract to change the role of the contract owner to any address by passing as an argument to the changeOwner function.
    function changeOwner(address _newOwner) public ownerOnly {
owner = _newOwner;
}
  • g. The next function deposit allows anyone to send a minimum of 1 ETH to the contract.
    function deposit() public payable {
require(
msg.value >= 0.01 * 10 ** 18,
"you need to send at least 0.01 ETH"
);
}
  • h. Finally, the last function in the Sample.sol contract allows anyone calling the contract to withdraw funds from the contract, as long as you pass in the number of tokens to withdraw as an argument. This transaction will also be terminated if the amount passed in exceeds than 10 ETH.
    function withdraw(uint256 _amount) public payable {
require(_amount <= 100000000000000000);
payable(msg.sender).transfer(_amount);
}

If you’ve completed your Sample.sol contract, Your smart contract should look exactly like the code below; You should update your contract with the code below for uniformity sake:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

contract Sample {
string public name;
address public owner;

constructor() {
owner = msg.sender;
name = "deployer";
}

function rename(string memory _name) public {
name = _name;
}

function describe() public view returns (string memory) {
return (name);
}

modifier ownerOnly() {
require(
msg.sender == owner,
"this function requires the owner of the contract to run"
);
_;
}

function changeOwner(address _newOwner) public ownerOnly {
owner = _newOwner;
}

function deposit() public payable {
require(
msg.value >= 0.01 * 10 ** 18,
"you need to send at least 0.01 ETH"
);
}

function withdraw(uint256 _amount) public payable {
require(_amount <= 100000000000000000);
payable(msg.sender).transfer(_amount);
}
}

To confirm you have no existing errors in your contract, run the command npx truffle compile on your terminal, and a successful result should look like the image below.

compiling_contract

Now that you know the different functions in the Sample.sol contract and you’re familiar with what they do. Next, you’ll learn how to create a unit test script to test subsections of the contract you just made.

Writing the Unit Test Script

Now that you have created the Sample.sol contract, you can begin writing the unit tests for the contract. After completing these tests, you’ll have a basic idea of how to create unit tests for smart contracts.

A very common pattern used when writing unit tests for smart contracts is:

a. Arrange: This is where you create dummy variables that you’ll need to run units of your test cases. They can be created globally after the contract test function s created or locally within the unit test.

b. Act: Next, is the part where you run your testing functions and store the result in a variable.

c. Assert: Since you already know the correct result of the test, then you compare your expected result with the response of the test you ran. If the test returns the expected result, it passes else, the test does not pass.

Also following the format:

 describe(<"functionName">, async function () {
beforeEach(async function() {
<what should happen before each test is run>
})
it("what the test is expected to do", async function () {
const response = <what was returned>
const result = <what should be returned>;
expect(response).to.equal(result); // compares the response to the expected result
});

Next, you’ll be creating a uint-test to test your Sample.sol contract using the previous format above, and you’ll learn how to create a basic unit test script on your own:

Testing a smart contract makes it easier to identify bugs and vulnerabilities and reduces the possibility of software errors that could lead to costly exploits. In the next few steps, you will learn the basic format of how to write unit tests based on your smart contract.

  • First, head over to the migrations folder and create a script file called 1_deploy_contract.js and copy the code below into the script.
const Sample = artifacts.require("Sample");
// const MetaCoin = artifacts.require("MetaCoin");

module.exports = function (deployer) {
// deployer.deploy(Sample);
// deployer.link(Sample, SampleTest);
// deployer.deploy(SamplTest);
deployer.deploy(Sample, { gas: 1000000 });
};

The code above is created to simply deploy your Sample.sol contract. Next, navigate to the test folder and create a new test script, SampleTest.js.

  1. Firstly, you’ll need to import the contract as a variable Sample in the first line of code.
const Sample = artifacts.require("Sample");
  1. Next, you’ll need to initialize the contract test with the following code below. This contract - Sample will cover all the unit test functions that will be carried out on the named contract.
contract("Sample", (accounts) => {
})
  1. Using the describe keyword to define a specific test for each function in the contract, you can carry out multiple tests using the it keyword for a specific function. The first test constructor tests the constructor function in the contract. Copy and add the code below.
  describe("constructor", async function () {
it("should have the correct name", async () => {
const sample = await Sample.deployed();
const name = await sample.name();
assert.equal(name, "deployer");
});

it("should have the correct owner", async () => {
const sample = await Sample.deployed();
const owner = await sample.owner();
assert.equal(owner, accounts[0]);
});
});

The function has two tests with string descriptions of what each of them is meant to do. The first test check for the initialization of the name variable and checks the value of the owner variable to the address of the deployer. The test passes if the result returns as expected and reverts with an error otherwise.

Now, run the command npx truffle test, and a successful result should look like the image below.

test(2)

  1. The next unit test describes the rename and describe function from the smart contract; the function carries out a single test on the rename and describe function. The test updates the name variable's value and checks the current the variable's current value if it has been updated. Copy and add the code below.
  describe("rename & describe", async function () {
it("should be able to rename", async () => {
const sample = await Sample.deployed();
await sample.rename("new name");
const name = await sample.describe();
assert.equal(name, "new name");
});
});

Now, run the command npx truffle test and a successful result should look like the image below.

test(1)

  1. The next unit test describes the changeOwner function in the smart contract; the test first uses the right address to attempt to change the owner, which should pass successfully. And then uses another random address to change the ownership role, which is meant to be reverted. Copy and add the code below.
describe("changeOwner", async function () {
it("should change the owner", async () => {
const sample = await Sample.deployed();
await sample.changeOwner(accounts[1], { from: accounts[0] });
const owner = await sample.owner();
assert.equal(owner, accounts[1]);
});

it("should not change the owner", async () => {
const sample = await Sample.deployed();
try {
await sample.changeOwner(accounts[2], { from: accounts[1]});
} catch (error) {
assert.equal(
error.message,
"VM Exception while processing transaction: revert"
)};
});
});

Now, run the command npx truffle test and a successful result should look like the image below.

test(3)

  1. The next function tests the deposit function of the contract. The first test will verify the deposit function works correctly which allows deposits of 0.01 ETH or greater. The second test verifies that the deposit function correctly rejects deposits of less than 0.01 ETH. Copy and paste the code below.
  describe("deposit", async function () {
it("should allow deposits", async () => {
const sample = await Sample.deployed();
await sample.deposit({ value: 0.01 * 10 ** 18 });
});
it("should not allow deposits below 0.01 ETH", async () => {
const sample = await Sample.deployed();
try {
await sample.deposit({ value: 0.009 * 10 ** 18 });
assert.fail("deposit should have failed");
} catch (error) {
assert.ok(error.message.includes("revert"));
}
});
});

Now, run the command npx truffle test and a successful result should look like the image below.

test(4)

  1. This next describe function tests the withdraw function in the contract. The first test is attempting to withdraw 0.01 ether from the contract. The second test is attempting to withdraw an amount greater than the balance to ensure that the withdrawal fails. If the test fails, it will return an error message with the word revert.
  describe("withdraw", async function () {
it("should allow withdrawals", async () => {
const sample = await Sample.deployed();
await sample.withdraw(BigInt(0.01 * 10 ** 18));
});
it("should not allow withdrawals above balance", async () => {
const sample = await Sample.deployed();
try {
await sample.withdraw(BigInt(0.01 * 10 ** 18));
assert.fail("withdrawal should have failed");
} catch (error) {
assert.ok(error.message.includes("revert"));
}
});
});

Finally, run the command npx truffle test and a successful result should look like the image below.

test(5)_

After completing your test script, your code should look exactly like the one below. For uniformity, sake replaces the entire code with this code test.

const Sample = artifacts.require("Sample");

contract("Sample", (accounts) => {
describe("constructor", async function () {
it("should have the correct name", async () => {
const sample = await Sample.deployed();
const name = await sample.name();
assert.equal(name, "deployer");
});

it("should have the correct owner", async () => {
const sample = await Sample.deployed();
const owner = await sample.owner();
assert.equal(owner, accounts[0]);
});
});

describe("rename & describe", async function () {
it("should be able to rename", async () => {
const sample = await Sample.deployed();
await sample.rename("new name");
const name = await sample.describe();
assert.equal(name, "new name");
});
});
describe("changeOwner", async function () {
it("should change the owner", async () => {
const sample = await Sample.deployed();
await sample.changeOwner(accounts[1], { from: accounts[0] });
const owner = await sample.owner();
assert.equal(owner, accounts[1]);
});

it("should not change the owner", async () => {
const sample = await Sample.deployed();
try {
await sample.changeOwner(accounts[2], { from: accounts[1] });
} catch (error) {
assert.equal(
error.message,
"VM Exception while processing transaction: revert"
);
}
});
});
describe("deposit", async function () {
it("should allow deposits", async () => {
const sample = await Sample.deployed();
await sample.deposit({ value: 0.01 * 10 ** 18 });
});
it("should not allow deposits below 0.01 ETH", async () => {
const sample = await Sample.deployed();
try {
await sample.deposit({ value: 0.009 * 10 ** 18 });
assert.fail("deposit should have failed");
} catch (error) {
assert.ok(error.message.includes("revert"));
}
});
});
describe("withdraw", async function () {
it("should allow withdrawals", async () => {
const sample = await Sample.deployed();
await sample.withdraw(BigInt(0.01 * 10 ** 18));
});
it("should not allow withdrawals above balance", async () => {
const sample = await Sample.deployed();
try {
await sample.withdraw(BigInt(0.01 * 10 ** 18));
assert.fail("withdrawal should have failed");
} catch (error) {
assert.ok(error.message.includes("revert"));
}
});
});
});

Conclusion

Writing unit tests for smart contracts can help a great deal in ensuring a secure and proficient contract, by suggesting fixes and improvements after discovering errors, issues, and security vulnerabilities in your contract. You have successfully created your unit test script for a simple sample contract using truffle. Now that you understand how unit tests are written, you can move on to writing more complex test scripts for other smart contracts. You can also read about how to run the unit test for smart contracts using Truffle.

Next Steps

Here is some other tutorial article.

Unit testing with Hardhat and Celo

How to create and Test contract calls with Celo and Hardhat

About the Author

Mayowa Julius Ogungbola

A Software Engineer and technical writer who is always open to working on new ideas. I enjoy working on GitHub, and you could also find out what I tweet about and connect with me on LinkedIn

References

Here is a link to the complete tutorial sample code on my GitHub, Leave a ⭐on the repository if you find it helpful.

Go back

· 14 min read
Glory Agatevure

Introduction​

This tutorial will show you how to create a simple Android app that allows users to make payments using the Celo Java SDK. The app, called "Buy Me A Coffee", will allow users to make a donation to any one of their choice using their Celo account. We will walk you through the process of setting up the Celo Java SDK and integrating it into your app, so you can start accepting payments in no time. By the end of this tutorial, you will have a working Android app that allows users to make payments using the Celo network.

He is a quick look at what we will build in this tutorial.

Fig: 0-1 dApp UI

dapp-ui

Prerequisites​

To successfully follow this tutorial, you will need basic knowledge of blockchain technology and Android development.

  • You will need to create five generated accounts using the celocli. To set this up, this guide will be helpful.

  • To test the functionality of the Celo Java SDK you will need to have some test tokens. This can be gotten from the faucet.

Requirements​

  • Alfajores Testnet Account - required to connect to the dApp and make test transactions.
  • Android Studio - required to build an app that runs on an emulator or a physical android phone. To run the completed code, the minimum version of Android Studio 5.0 or newer and
  • Java SDK version 11 or higher

Workspace Setup and Configuration in Android Studio

To get started, your Android Studio should be up and running. Add all necessary dependencies to your app/build.gradle file. To start using the Celo Java SDK, here are the dependencies you would need.

implementation 'io.github.gconnect:celo-sdk-java:0.0.2'
implementation 'org.web3j:core:5.0.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'

Celo Java SDK - is a Java library for working with the Celo Blockchain, Contractkit and Celo Core Contracts.

Web3j is a highly modular, reactive, type-safe Java and Android library for working with Smart Contracts and integrating with clients (nodes) on the Ethereum network:

Circleimageview library is used for making an image circular.

Create an Android project

Start up Android Studio and create a new Project. Select basic activity.

Fig 1- 0: Create New Project Create New Project

Fig 1-1: Create New Basic Activity Create New Project

Note: Constant.java file holds the constants for the app.

Account Creation

Celo provides us with several options to create an account. These include downloading the mobile wallet (Valora or Alfajores), and using Ganache and Celo CLI. For development purposes, any of these options will be good to easily create or generate Celo accounts. For this tutorial, we will use the Celo CLI to generate 5 Celo Testnet accounts. For more details on account creation, you can check the Celo Developer Doc for Testnet Wallet Setup.

To set up the Celo CLI on your machine, follow this guide. After successful setup, from your terminal, run this command to create a new account.

celocli account:new

Funding an Account

To fund your Celo Testnet account, copy the address of the wallet or from your generated account after running the above command. Simply go to the faucet and paste the address to get it funded. If you need more funds, paste your address in the input field and check the captcha and click on the get started button. That should get your account funded immediately.

Fig 1-3: Celo Faucet Faceut

Checking Account Balance

There are several ways to check your account balance. You can use the Celocli command, the Celo Java SDK or simply enter your account address in the Alfajores Celo Scan explorer.

Using the Celocli run this command to check account balance

celocli account:balance 0x93312861A9844E5E62F3730CAA913DD11adE75b9

Output should look like this 👇

Fig 2-1: Celocli Output Celocli account balance

Checking balance using the Alfajores Celo Scan explorer output should look like this 👇

Fig 2-2: Alfajores Celo Scan explorer. Celo scan

Checking balance using using the Celo Java-SDK required code snippet should look like this.👇

ContractKit contractKit = ContractKit.build(new HttpService("https://alfajores-forno.celo-testnet.org"));
Web3j web3j = Web3j.build(new HttpService(ContractKit.ALFAJORES_TESTNET));

ContractKitOptions config = new ContractKitOptions.Builder()
.setFeeCurrency(CeloContract.StableToken)
.setGasPrice(BigInteger.valueOf(21_000))
.build();
contractKit = new ContractKit(web3j, config);

// Multiple accounts can be added to the kit wallet. The first added account will be used by default.
contractKit.addAccount(Constant.somePrivateKey);

// To change default account to sign transactions
contractKit.setDefaultAccount(Constant.publicKey);

AccountBalance balance = contractKit.getTotalBalance(Constant.myAddress);
System.out.println("cUSD balance " + balance.cUSD);
System.out.println("Celo balance " + balance.CELO);

The above code snippet can be found in the FirstFragment.java file in the complete code of the project repository.

Note: This code should not be run on the main UI thread otherwise, the app will scratch. This is so because the above code requires making network calls. To keep things simple, we are running the above code in a runnable thread.

The output will look like this 👇 Account balance log

Manifest.xml

Inside your manifest.xml file add network permission and network state. This will prevent the app from crashing. And it also notifies the app that internet connection is required.

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Data Model, Data Source, Constant and Recyclerview Adapter

Data Model

The BeneficiaryModel.java file handles the data model popularly called pojo. This defines the initial data object.

package com.example.celocoffeeapp;

import android.os.Parcel;
import android.os.Parcelable;

public class BeneficiaryModel implements Parcelable {
private int imgId;
private String name;
private String description;
private String walletAddress;

public BeneficiaryModel(int imgId, String name, String description,
String walletAddress) {
this.imgId = imgId;
this.name = name;
this.description = description;
this.walletAddress = walletAddress;
}

protected BeneficiaryModel(Parcel in) {
imgId = in.readInt();
name = in.readString();
description = in.readString();
walletAddress = in.readString();
}

public static final Creator<BeneficiaryModel> CREATOR = new Creator<BeneficiaryModel>() {
@Override
public BeneficiaryModel createFromParcel(Parcel in) {
return new BeneficiaryModel(in);
}

@Override
public BeneficiaryModel[] newArray(int size) {
return new BeneficiaryModel[size];
}
};

public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getImgId() {
return imgId;
}
public void setImgId(int imgId) {
this.imgId = imgId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getWalletAddress() {
return walletAddress;
}

public void setWalletAddress(String walletAddress) {
this.walletAddress = walletAddress;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(imgId);
dest.writeString(name);
dest.writeString(description);
dest.writeString(walletAddress);
}
}

Constant

This handles constants and can be found in constant.java file.

package com.example.celocoffeeapp;

public class Constant {
static final String address1 = "0xCB1b7902253a02ac7F977d470681F518AcA362Fa";
static final String address2 = "0x9799a1530Fc0f0A8FD4Ce036e8B0D99d22Ef9d5a";
static final String address3 = "0xE1f35f42b09482C30e114C16b9F54d2033d79980";
static final String address4 = "0x70E7249F304062cACc03A018f574B5C3d0b43466";
static final String address5 = "0x2f3d7F1804c6A4A8dC8Df271848aA94240F9cdD5";
static final String myAddress = "0xCb1fe4ABF5E3218f749095D4E950f39264F0485d";
static final String somePrivateKey = "4dcc51d372e5a8303ddace99a625ecce8b7fcee4e6a88cbab4acc8f5fb39fea9";
static final String publicKey = "03cbc12902c23a2287365d15a3a2f102f1e2ed5b8712dd0a7b44f797336bd7b50e";
static final String someAddress = "0x93312861A9844E5E62F3730CAA913DD11adE75b9";
}

Data Source

This is the local data source, used to populate the data in the recyclerview. The data source can be found in the DataSource.java file.

package com.example.celocoffeeapp;

public class DataSource {
public static BeneficiaryModel[] myListData = new BeneficiaryModel[] {
new BeneficiaryModel(
R.drawable.img1,
"John Doe",
"Software Engineer",
Constant.address1
),
new BeneficiaryModel(
R.drawable.img2,
"Ronald Clit",
"Product Designer",
Constant.address2
),
new BeneficiaryModel(
R.drawable.img3,
"Allen Smith",
" Technical Writer",
Constant.address3
),
new BeneficiaryModel(
R.drawable.img4,
"Justin Cooper",
"Celo Developer",
Constant.address4
),

new BeneficiaryModel(
R.drawable.img5,
"Jane Doe",
"Dev Rel",
Constant.address5
),
};
}

Recyclerview Adapter

This handles the populating of the list. The Recyclerview adapter can be found in the BeneficiaryAdapter.java file.

package com.example.celocoffeeapp;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;


public class BeneficiaryAdapter extends RecyclerView.Adapter<BeneficiaryAdapter.ViewHolder>{
private BeneficiaryModel[] listdata;
private Context context;
private ItemClickListener itemClickListener;

public BeneficiaryAdapter(ItemClickListener clicklistener, Context context, BeneficiaryModel[] listdata) {
this.listdata = listdata;
this.context = context;
this.itemClickListener = clicklistener;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View listItem = layoutInflater.inflate(R.layout.beneficiary_list_item, parent, false);
ViewHolder viewHolder = new ViewHolder(listItem);
return viewHolder;
}

@Override
public void onBindViewHolder(ViewHolder holder, @SuppressLint("RecyclerView") int position) {
final BeneficiaryModel myListData = listdata[position];
holder.imageView.setImageResource(listdata[position].getImgId());
holder.name.setText(listdata[position].getName());
holder.description.setText(listdata[position].getDescription());
holder.donation_received.setText(String.valueOf(String.format("Donations Received: %s", BeneficiaryAccountBalance.balance(position, listdata))));

holder.button.setText(String.format(listdata[position].getWalletAddress().substring(0, 5) + "..." + "%s" , "Donate"));
holder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(view.getContext(), "click on item: " + myListData.getName(), Toast.LENGTH_LONG).show();
itemClickListener.onItemClicked(myListData);
}
});
}

@Override
public int getItemCount() {
return listdata.length;
}

public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView name, description, donation_received;
public Button button;
public ConstraintLayout constraintLayout;

public ViewHolder(View itemView) {
super(itemView);
this.imageView = (ImageView) itemView.findViewById(R.id.profile_image);
this.name = (TextView) itemView.findViewById(R.id.name);
this.description = (TextView) itemView.findViewById(R.id.description);
this.donation_received = (TextView) itemView.findViewById(R.id.donations_received);
this.button = (Button) itemView.findViewById(R.id.donateBtn);
constraintLayout = (ConstraintLayout) itemView.findViewById(R.id.constraint);

}
}

public interface ItemClickListener {
public void onItemClicked(BeneficiaryModel listData);
}
}

Custom Method For Getting the Account Balance of Each of the Creators in the List

This method requires connecting to the Celo Afajores Testnet Network. To get the account balance for each of the recipients in the recyclerview. This can be found in the BeneficiaryAccountBalance.java file.

package com.example.celocoffeeapp;

import org.celo.contractkit.CeloContract;
import org.celo.contractkit.ContractKit;
import org.celo.contractkit.ContractKitOptions;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class BeneficiaryAccountBalance {
public static BigDecimal balance(int position, BeneficiaryModel[] listdata) {
ExecutorService executor = Executors.newSingleThreadExecutor();
final BigInteger[] donationReceived = {BigInteger.valueOf(0)};

Future<Long> result = executor.submit(new Callable<Long>() {
@Override
public Long call() throws Exception {
// getting the balance of each of the address

String listAddress = listdata[position].getWalletAddress();
ContractKit contractKit = ContractKit.build(new HttpService("https://alfajores-forno.celo-testnet.org"));
Web3j web3j = Web3j.build(new HttpService(ContractKit.ALFAJORES_TESTNET));
ContractKitOptions config = new ContractKitOptions.Builder()
.setFeeCurrency(CeloContract.GoldToken)
.setGasPrice(BigInteger.valueOf(21_000))
.build();
contractKit = new ContractKit(web3j, config);
org.celo.contractkit.AccountBalance balance = null;
try {
balance = contractKit.getTotalBalance(listAddress);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("cUSD balance " + balance.cUSD);
donationReceived[0] = balance.cUSD;
return donationReceived[0].longValue();
}
});
while (!result.isDone()) {
try {
// System.out.println("Waiting for the Future to complete ...");
Long returnValue = result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}

return Convert.fromWei(donationReceived[0].toString(), Convert.Unit.ETHER);
}
}

FirstFragment.java

In the FirstFragment.java file, inside the onViewCreated add the below code snippet. This will populate the local datasource in the recyclerview.

BeneficiaryModel[] myListData =  DataSource.myListData;

BeneficiaryAdapter adapter = new BeneficiaryAdapter(this,getContext(), myListData);
binding.recyclerview.setHasFixedSize(true);
binding.recyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerview.setAdapter(adapter);


binding.swipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
binding.recyclerview.setHasFixedSize(true);
binding.recyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerview.setAdapter(adapter);
binding.swipe.setRefreshing(false);

}

Implement the ItemOnclickListener interface to the FirstFragment.java file. This will implement the onItemClicked method. Add the below code. This will enable clicking to the detail page.

Bundle bundle = new Bundle();
bundle.putParcelable("data", listData);
System.out.println(listData);
NavHostFragment.findNavController(FirstFragment.this)
.navigate(R.id.action_FirstFragment_to_SecondFragment, bundle );

SecondFragment.java - Make Payment

Your detail page code snippet should look like this. This fragment handles the detail page of each of the creators/beneficiaries. This also handles payment using deeplink making interactions with the Valora or Alfajores wallet.

package com.example.celocoffeeapp;

import static android.content.ContentValues.TAG;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import com.example.celocoffeeapp.databinding.FragmentSecondBinding;

import java.util.Objects;

public class SecondFragment extends Fragment {

private FragmentSecondBinding binding;
private BeneficiaryModel model;
String inputAmount;
String inputComment;

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {

binding = FragmentSecondBinding.inflate(inflater, container, false);

assert getArguments() != null;
model = getArguments().getParcelable("data");
String s = "Buy %s a coffee";
binding.textviewName.setText(String.format(s, model.getName()));
binding.textviewDescription.setText(model.getDescription());
inputAmount = Objects.requireNonNull(binding.amountInput.getText()).toString().trim();
inputComment = Objects.requireNonNull(binding.commentInput.getText()).toString().trim();

binding.amountInput.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// you can call or do what you want with your EditText here
// yourEditText...
}

public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}

public void onTextChanged(CharSequence s, int start, int before, int count) {
System.out.println("text" + s);
binding.buttonSecond.setText(new StringBuilder().append("Support ").append("( cUSD").append(s).toString() + " )");
inputAmount = s.toString();
}
});


binding.buttonSecond.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (inputAmount.isEmpty()) {
binding.amountLayout.setError("Field required!");
return;
} else {
makePaymentWithCeloWallet();
}
}
});
return binding.getRoot();

}

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}

public void makePaymentWithCeloWallet() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
String walletAddress = model.getWalletAddress();
String url = "celo://wallet/pay?address=" + walletAddress + "&displayName=Buy%20Me%20A%20Coffee&amount=" + inputAmount + "&comment=" + inputComment + "&token=cUSD&currencyCode=USD";
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
} catch (Exception e) {
Log.i(TAG, "run: " + e);

getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), "Valora app not installed, please install...", Toast.LENGTH_SHORT).show();
}
});
}
}
});
thread.start();
}
}

Fig: 3-1 Payment UI payment pop-up payment page

Android Views

This consists of the XML files which handles the UIs.

Beneficiary List Item The beneficiary_list_item.xml file is used in the BeneficiaryAdapter to display the List items. The UI looks like this 👇

Fig 3-2 List Page landing-page

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/constraint"
android:padding="8dp"
xmlns:app="http://schemas.android.com/apk/res-auto">

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="24dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/profile_image"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:textSize="18sp"
android:textStyle="bold"
/>

<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/name"
app:layout_constraintLeft_toLeftOf="@id/name"
android:layout_marginTop="8dp"

/>

<TextView
android:id="@+id/donations_received"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/description"
app:layout_constraintLeft_toLeftOf="@id/description"
/>

<com.google.android.material.button.MaterialButton
android:id="@+id/donateBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Donate"
app:cornerRadius="16dp"
android:textColor="@color/white"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/donations_received"
app:layout_constraintLeft_toLeftOf="@id/donations_received"
android:background="@drawable/button"
android:paddingLeft="16dp"
android:paddingRight="16dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

fragment_first.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">

<TextView
android:id="@+id/textview_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_first_fragment"
android:textSize="18sp"
android:textStyle="bold"
android:lineHeight="30sp"
android:letterSpacing="0.01"
android:layout_marginTop="24dp"
android:textAlignment="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textview_first"
>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="60dp"
tools:listitem="@layout/beneficiary_list_item"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swipe"
android:paddingBottom="80dp"
/>

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

fragment_second.xml This is the detail fragment. And the UI looks like this

Fig 3-3 Detail Page detail-page

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment">

<TextView
android:id="@+id/textview_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buycoffee"
android:layout_margin="24dp"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/textview_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="description"
android:layout_marginTop="16dp"
app:layout_constraintLeft_toLeftOf="@id/textview_name"
app:layout_constraintTop_toBottomOf="@id/textview_name" />

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/textview_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/img"
app:layout_constraintLeft_toLeftOf="@id/textview_description"
app:layout_constraintTop_toBottomOf="@id/textview_description"
android:layout_marginTop="24dp"
/>

<TextView
android:id="@+id/textview_x"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X"
android:textSize="40sp"
android:layout_marginTop="24dp"
android:layout_marginLeft="18dp"
app:layout_constraintLeft_toRightOf="@id/textview_icon"
app:layout_constraintTop_toBottomOf="@id/textview_description" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/amount_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="24dp"
style="@style/MyTextInputLayout"
app:layout_constraintLeft_toRightOf="@id/textview_x"
app:layout_constraintTop_toBottomOf="@id/textview_description"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/amount_input"
android:hint="Amount"
android:textColor="@color/black"
android:inputType="numberDecimal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/comment_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp"
style="@style/MyTextInputLayout"
app:layout_constraintLeft_toLeftOf="@id/textview_icon"
app:layout_constraintTop_toBottomOf="@id/amount_layout"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/comment_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:lines="8"
android:textColor="@color/black"
android:maxLines="10"
android:scrollbars="vertical"
android:hint="Say something nice ( Optional )"
/>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/button_second"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/support"
android:layout_margin="24dp"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/comment_layout" />

<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

Interacting and Logging Output of the Celo Java SDK Contract Kit

The below code snippet can be found in FirstFragment.java file. To test the functionalities of the code, we will be logging and displaying the output in a screenshot.

// deploy contract
GoldToken deployedGoldenToken = null;
try {
deployedGoldenToken = contractKit.contracts.getGoldToken().deploy().send();
} catch (Exception e) {
e.printStackTrace();
}
//
// // Get transaction receipt
TransactionReceipt receipt = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
receipt = Objects.requireNonNull(deployedGoldenToken).getTransactionReceipt().get();
if (receipt != null) {
String hash = receipt.getTransactionHash();
System.out.println("hash " + hash);
}

//Buying celo with cusd
ExchangeWrapper exchange = contractKit.contracts.getExchange();
StableTokenWrapper stableToken = contractKit.contracts.getStableToken();
GoldTokenWrapper goldToken = contractKit.contracts.getGoldToken();
System.out.println( "gold token" + goldToken);

BigInteger cUsdBalance = stableToken.balanceOf(Constant.myAddress).send();
System.out.println( "cUsdBalance " + cUsdBalance);

TransactionReceipt approveTx = stableToken.approve(exchange.getContractAddress(), cUsdBalance).send();
String approveTxHash = approveTx.getTransactionHash();
System.out.println( "Approve stabletoken Hash" + approveTxHash);

BigInteger goldAmount = exchange.quoteUsdSell(cUsdBalance).send();
System.out.println("goldAmount" + goldAmount);

TransactionReceipt sellTx = exchange.sellDollar(cUsdBalance, goldAmount).send();
String sellTxHash = sellTx.getTransactionHash();
System.out.println( "Exchange Hash" + sellTxHash);

// Interacting with CELO & cUSD
GoldTokenWrapper goldtoken = contractKit.contracts.getGoldToken();
BigInteger goldBalance = goldtoken.balanceOf(Constant.myAddress);
System.out.println( "goldBalance " + Convert.fromWei(goldBalance.toString(), Convert.Unit.ETHER));

// Send GoldToken fund
BigInteger oneGold = Convert.toWei(BigDecimal.ONE, Convert.Unit.ETHER).toBigInteger();
TransactionReceipt tx = goldtoken.transfer(Constant.someAddress, oneGold).send();
String hash = tx.getTransactionHash();
System.out.println( "trans hash " + hash);

// Contract Addresses
String goldTokenAddress = contractKit.contracts.addressFor(CeloContract.GoldToken);
System.out.println("goldAddress " + goldTokenAddress);


CeloRawTransaction tx1 = CeloRawTransaction.createCeloTransaction(
BigInteger.ZERO,
GAS_PRICE,
GAS_LIMIT,
Constant.someAddress,
BigInteger.ONE,
contractKit.contracts.addressFor(CeloContract.StableToken),
null,
null
);
EthSendTransaction receipt1 = contractKit.sendTransaction(tx1, Constant.myAddress);
System.out.print( "reeipt1 " + (receipt1 != null ? receipt1.getTransactionHash() : null));
Log.i(TAG, "receipt1 " + gson.toJson(receipt1, EthSendTransaction.class ));

Fig 3-4 Output transaction-hash

Conclusion​

Congratulations on finishing this tutorial! Thank you for taking the time to complete it. In this tutorial, you have learned how to create an Android dApp from scratch using the Celo SDk-Java and how to handle deep linking to connect to the Valora or Alfajores wallet to make payments.

To have access to the full codebase, here is the link to the project repo on github.

About the Author​

Glory Agatevure is a blockchain engineer, technical writer, and co-founder of Africinnovate. You can connect with me on Linkedin, Twitter and Github.

References​

Go back

· 19 min read

Introduction

In this article, we will provide a comprehensive comparison of proof of work and proof of stake, highlighting the key differences between the two algorithms and discussing their respective advantages and disadvantages. We will also examine the potential future developments of these algorithms and consider their potential use cases in various blockchain applications.

Importance of Consensus Algorithms in Blockchain Technology

Consensus algorithms are an essential component of blockchain technology because they help ensure the integrity, security, and reliability of the blockchain network. These algorithms help to prevent double spending, malicious attacks, and other forms of tampering or fraud. Some of the key benefits of consensus algorithms in blockchain technology include:

  • Ensuring the Integrity of the Blockchain

Consensus algorithms help to ensure that the data on the blockchain is accurate, consistent, and cannot be tampered with. This is critical for maintaining the trust and reliability of the blockchain.

  • Enhancing Security

Consensus algorithms help to secure the blockchain by requiring multiple parties to agree on the validity of transactions before they are added to the blockchain. This makes it difficult for attackers to alter or manipulate the data on the blockchain.

  • Improving Scalability

Consensus algorithms can help to improve the scalability of a blockchain by allowing transactions to be processed and verified more quickly and efficiently. This is especially important for large, decentralized networks that need to handle a high volume of transactions.

  • Facilitating Decentralization

Consensus algorithms help to ensure that no single party has complete control over the blockchain. This helps to promote decentralization and ensures that the blockchain is not controlled by a single entity.

  • Promoting Trust

Consensus algorithms help to build trust among participants in the blockchain network, as they provide a transparent and secure way to verify and validate transactions. This helps to foster a sense of confidence and security among users of the blockchain.

Proof of Work

Proof of work (PoW) is a consensus algorithm that is used by many blockchain networks, including the original and most well-known cryptocurrency, Bitcoin. PoW requires participating nodes (also called "miners") to solve a complex mathematical puzzle to validate transactions and create new blocks on the blockchain. The process of solving these puzzles is known as "mining."

To mine a block, miners must find a solution to the puzzle that meets certain predetermined criteria. This process involves using powerful computer hardware to perform a large number of calculations in a process called "hashing." When a miner successfully finds a solution to the puzzle, they can add a new block to the blockchain and receive a reward in the form of cryptocurrency.

PoW is designed to be resource-intensive and time-consuming so it is not easy for a single miner or group of miners to dominate the process. Instead, it requires a decentralized network of miners to work together to validate transactions and create new blocks. This helps to ensure the security and integrity of the blockchain, as it is very difficult for a single entity to manipulate or alter the data on the blockchain.

One of the key benefits of PoW is that it is a proven and well-established consensus algorithm that has been used successfully for many years. It has a strong track record of security and reliability, and it is resistant to tampering and other forms of fraud. However, PoW also has some downsides, including the fact that it is resource-intensive and requires a significant amount of energy to power the mining process. Some blockchain networks have sought to address these issues by implementing alternative consensus algorithms that are less resource-intensive and more energy-efficient.

How Does It Works?

In a Proof of Work (PoW) consensus algorithm, miners are required to perform a certain amount of work to validate transactions and create new blocks on the blockchain. This work typically involves solving complex mathematical puzzles, which require the use of powerful computer hardware to perform a large number of calculations.

The process of solving these puzzles is known as "mining." Miners use their computer hardware to perform a series of calculations, called "hashing," in an attempt to find a solution to the puzzle that meets certain predetermined criteria. When a miner finds a solution, they can add a new block to the blockchain and receive a reward in the form of cryptocurrency.

The difficulty of the puzzles is carefully calibrated so that it takes a certain amount of time, on average, for miners to find a solution. This helps to ensure that the process of mining new blocks is not too easy or too difficult and that it is decentralized. If the puzzles were too easy, a single miner or group of miners could dominate the process and create most of the new blocks, which would undermine the security and integrity of the blockchain. On the other hand, if the puzzles were too difficult, it would take too long for new blocks to be created, and the blockchain would not be able to process transactions on time.

To participate in the mining process, miners must compete with one another to find a solution to the puzzle. The first miner to find a solution can add the new block to the blockchain and receive the reward. This creates an incentive for miners to compete with one another and contribute their computing power to the network.

Advantages of Proof of Work

  • Security

PoW is a secure and reliable consensus algorithm that has been used successfully for many years. It is resistant to tampering and other forms of fraud, and it helps to ensure the integrity and security of the blockchain.

  • Decentralization

PoW requires a decentralized network of miners to work together to validate transactions and create new blocks. This helps to prevent any single miner or group of miners from dominating the process and ensures that the blockchain is not controlled by a single entity.

  • Energy Efficiency

PoW algorithms are designed to be resource-intensive and time-consuming, which helps to ensure that they are decentralized and secure. However, this can also make them energy-intensive, as miners need to use a lot of electricity to power their computer hardware.

  • Incentives for Miners

PoW provides an incentive for miners to contribute their computing power to the network. When a miner successfully solves a puzzle and adds a new block to the blockchain, they receive a reward in the form of cryptocurrency. This helps to encourage miners to participate in the mining process and contribute to the security of the blockchain. Widely-adopted PoW is a well-established and widely adopted consensus algorithm, and it is used by many of the most well-known blockchain networks, including Bitcoin. This means that it has a strong track record of security and reliability.

Disadvantages of Proof of Work

  • Resource-intensive

PoW algorithms are designed to be resource-intensive and time-consuming, which can make them expensive and energy-intensive to operate. This can make it difficult for smaller, less well-funded miners to compete with larger, better-equipped miners.

  • Energy Consumption

As mentioned above, PoW algorithms can be energy-intensive, as they require a lot of electricity to power the mining process. This can be a concern for those who are concerned about the environmental impact of cryptocurrency mining.

  • Centralization

Since PoW algorithms are resource-intensive, they can be more expensive and difficult for smaller miners to participate in. This can lead to a degree of centralization, with a few large miners dominating the mining process. This can undermine the decentralization and security of the blockchain.

  • Scalability

PoW algorithms can be slower and less efficient than some alternative consensus algorithms, which can make it difficult for the blockchain to scale and handle a large volume of transactions.

  • Risk of 51% Attacks

If a single miner or group of miners controls more than 50% of the mining power on a PoW blockchain, they could potentially launch a 51% attack, which could allow them to alter the data on the blockchain or reverse transactions. While this is a rare occurrence, it is a potential risk to PoW algorithms.

Proof of Stake

Proof of Stake (PoS) is a consensus algorithm that is used to validate transactions and achieve distributed consensus in a blockchain network. It is an alternative to the more widely known Proof of Work (PoW) algorithm, which is used by networks such as Bitcoin and Ethereum.

In a PoS system, the validation of transactions and the creation of new blocks are not done by miners using powerful computers to solve complex mathematical problems, but rather by users who hold a stake in the network. These users, known as "validators," are chosen to create new blocks based on their stake, or the amount of cryptocurrency they hold in the network.

One of the main benefits of PoS is that it is much more energy efficient than PoW, as it does not require the use of expensive and power-hungry hardware to solve complex mathematical problems. This makes it an attractive option for blockchain networks that are looking to reduce their energy consumption and carbon footprint.

Another advantage of PoS is that it is more decentralized than PoW, as it does not rely on the use of powerful computers to validate transactions. This means that anyone with a stake in the network can become a validator, regardless of their computing power.

However, PoS also has its limitations. For example, it can be vulnerable to attacks from users with a large stake in the network, who may be able to influence the validation of transactions in their favor. There are also concerns about the centralization of PoS networks, as the concentration of stake in the hands of a few large holders could give them disproportionate influence over the network.

Despite these limitations, PoS has gained widespread adoption in the blockchain industry and is used by several major networks, including Cosmos, Tezos, and Ethereum 2.0. It is likely to continue to play a significant role in the development of decentralized networks in the future.

How It Works?

In a Proof of Stake (PoS) system, the process of validating transactions and creating new blocks is known as "staking." Validators, or "stakers," are chosen to create new blocks based on their stake, or the amount of cryptocurrency they hold in the network.

When a new block is ready to be added to the blockchain, the PoS algorithm selects a validator to create the block using a randomized process that is based on their stake in the network. This process is known as "slot assignment."

Once a validator has been selected to create a new block, they must validate the transactions in the block to ensure that they are all valid. If the transactions are valid, the validator can then add the block to the blockchain and receive a reward for their efforts.

One important aspect of PoS is the concept of "bonding," which is the process of locking up a certain amount of cryptocurrency as a stake in the network. This bonding process helps to ensure that validators are incentivized to act in the best interests of the network, as they stand to lose their stake if they act maliciously or try to cheat the system.

There are also mechanisms in place to help prevent malicious behavior in PoS systems, such as "slashing," which is the process of penalizing validators who act in ways that are detrimental to the network. This could include attempting to double spend, creating invalid blocks, or failing to follow the rules of the network.

Overall, the PoS consensus algorithm is designed to be energy efficient, decentralized, and secure, making it a popular choice for many blockchain networks. It is likely to continue to evolve and be refined as the blockchain industry grows and develops.

Advantages of Proof of Stake

  • Energy Efficiency

PoS algorithms do not require miners to perform resource-intensive computations to validate transactions and create new blocks, which means they are less energy-intensive compared to proof-of-work (PoW) algorithms. This makes PoS more environmentally friendly and sustainable in the long term.

  • Greater Decentralization

In a PoS system, anyone with a stake in the network can participate in the validation process, which means that the network is less centralized and more resistant to attacks and censorship.

  • Faster Transaction Processing

PoS algorithms generally allow for faster transaction processing and block creation compared to PoW, which can make them more suitable for networks with high transaction volumes.

  • Increased Security

PoS algorithms rely on a system of stakes and rewards to incentivize network participants to act honestly and maintain the integrity of the network. This can make PoS more resistant to malicious attacks and increases overall security.

  • Lower Entry Barriers

Because PoS algorithms do not require specialized mining hardware or a lot of energy, they can be more accessible to a wider range of participants, which can help to increase decentralization and foster a more diverse and resilient network.

Disadvantages of Proof of Stake

  • Dependence on Stake

In PoS systems, the amount of stake a validator has is directly proportional to their likelihood of being chosen to produce the next block. This can create a dependency on stake, and those with more stake may have a disproportionate influence on the network.

  • Lack of Miner Diversity

In PoW systems, miners are incentivized to participate in the network with the potential to earn block rewards. In PoS systems, there are often fewer rewards available because validators are chosen based on their stake rather than their computational power. This can lead to a lack of miner diversity and potentially less decentralization.

  • Complexity

Implementing a PoS system can be more complex than a PoW system because it requires a way to determine the stake of each validator and choose the next block producer based on that stake.

  • Lack of Security

PoS systems rely on the assumption that validators act honestly and follow the protocol. If a validator becomes malicious or is hacked, it can compromise the security of the network.

  • Vulnerability to Attacks

PoS systems may be more vulnerable to certain types of attacks, such as a "nothing at stake" attack, in which validators have no penalty for validating multiple competing chains. This can lead to network instability.

  • Limited Scalability

PoS systems may be less scalable than PoW systems because they often require more communication between nodes to reach a consensus. This can limit the overall transaction throughput of the network.

Comparison of Proof of Work and Proof of Stake

  • Consensus Mechanism

PoW relies on miners solving computational puzzles, also known as "hashing," to produce new blocks and reach a consensus on the state of the blockchain. This process is known as "mining." In contrast, PoS involves choosing the next block producer (validator) based on their stake in the network. Validators are selected in a deterministic way, often using a randomized selection process, and they are responsible for validating transactions and adding them to the blockchain.

  • Energy Consumption

PoW algorithms, such as Bitcoin's, consume a significant amount of energy due to the computational power needed to solve the puzzles. This energy consumption is necessary to secure the network and prevent double-spending. In contrast, PoS algorithms do not require miners to perform energy-intensive computations, which makes them more energy efficient.

  • Security

PoW relies on the assumption that it is computationally infeasible for a single miner to produce a majority of the blocks, which helps to secure the network. In contrast, PoS relies on the assumption that validators have a financial incentive to act honestly and follow the protocol. Validators are chosen based on their stake in the network, and they stand to lose their stake if they act maliciously. This can potentially increase the security of the network.

  • Centralization

PoW systems can potentially be more centralized because mining power is often concentrated among a few large mining pools. These mining pools can have a disproportionate influence on the network because they control a significant portion of the mining power. In contrast, PoS systems may be more decentralized because anyone with a stake in the network can potentially become a validator. This can lead to a more distributed network with no single point of failure.

  • Transaction Processing Speed

PoW systems may have slower transaction processing speeds because miners must perform computations to produce new blocks. This can lead to longer confirmation times for transactions. In contrast, PoS systems may have faster transaction processing speeds because they do not require miners to perform computations. This can potentially increase the overall throughput of the network.

  • Rewards

In PoW systems, miners are incentivized to participate in the network through block rewards, which are paid out in the native cryptocurrency of the network. These rewards help to secure the network and incentivize miners to contribute their computational power. In PoS systems, validators may receive a portion of transaction fees as a reward for their work validating transactions and adding them to the blockchain.

  • Barrier to Entry

In PoW systems, miners need specialized hardware, such as ASICs (application-specific integrated circuits), to compete for block rewards. These specialized devices can be expensive and may require a significant upfront investment. In contrast, anyone with a stake in a PoS system can potentially become a validator, which can lower the barriers to entry for participating in the network.

  • Complexity

Implementing a PoW system may be simpler because it only requires miners to perform computational work. In contrast, implementing a PoS system can be more complex because it requires a way to determine the stake of each validator and choose the next block producer based on that stake. This can involve the use of complex algorithms and data structures.

  • Vulnerability to Attacks (continued)

In contrast, PoS systems may be more vulnerable to certain attacks, such as a "nothing at stake" attack, in which validators have no penalty for validating multiple competing chains. This can lead to network instability and can potentially compromise the security of the network.

  • Scalability

PoW systems may be more scalable because they do not require as much communication between nodes to reach a consensus. This can allow for higher transaction throughput and faster confirmation times. In contrast, PoS systems may be less scalable because they often require more communication between nodes to reach a consensus. This can lead to slower confirmation times and lower overall transaction throughput.

Potential Future Developments

Proof of work (PoW) and proof of stake (PoS) are two different methods that have been developed to achieve distributed consensus in a decentralized network. In a PoW system, miners compete to solve a computationally difficult problem, and the first miner to solve the problem gets to add a new block to the blockchain and receives a reward for their work. In a PoS system, the creator of a new block is chosen based on their stake in the network, rather than their ability to solve a computational problem.

One potential future development for PoW algorithms is the use of more specialized hardware, such as application-specific integrated circuits (ASICs), to improve mining efficiency. However, the use of ASICs can lead to the centralization of the mining process, as only those with access to expensive ASICs can compete effectively. To address this issue, some PoW cryptocurrencies, such as Ethereum, are considering moving to a proof of work algorithm that is resistant to ASICs, known as a "memory-hard" algorithm.

Another potential development for PoW is the use of renewable energy sources to power the mining process. Currently, a significant portion of the energy used for PoW mining comes from fossil fuels, which has led to concerns about the environmental impact of cryptocurrency mining. Using renewable energy sources could help to address this issue and make cryptocurrency mining more sustainable.

As for PoS, one potential future development is the use of "delegated" proof of stake, in which a group of "validators" is responsible for creating new blocks on the blockchain. Validators are chosen based on their stake in the network and their reputation, and they are responsible for ensuring the integrity of the network. This approach can potentially offer faster transaction times and lower energy consumption compared to PoW.

Another potential development for PoS is the use of "adaptive" proof of stake, in which the probability of a validator being chosen to create a new block is based on both their stake and their activity on the network. This can incentivize validators to actively participate in the network and contribute to its security.

It is worth noting that both PoW and PoS algorithms are constantly evolving, and it is difficult to predict exactly what future developments will occur. However, both algorithms will likely continue to be refined and improved to make decentralized networks more efficient, secure, and sustainable.

Conclusion

Proof of Work (PoW) and Proof of Stake (PoS) are two algorithms that are used to secure and validate transactions on a blockchain network. PoW is the most widely used algorithm, and it relies on miners solving complex mathematical problems to validate transactions and add new blocks to the chain. PoS, on the other hand, relies on users holding a certain amount of the cryptocurrency to "stake" their tokens and validate transactions.

People need to understand these algorithms because they play a crucial role in the security and integrity of blockchain networks. PoW and PoS serve as mechanisms to prevent malicious actors from tampering with the network, as it requires a significant amount of resources to solve mathematical problems or hold a large number of tokens. Understanding these algorithms can also help individuals make informed decisions about which cryptocurrency to invest in, as different networks may use different consensus mechanisms.

Overall, PoW and PoS are important concepts for people to understand to fully grasp the inner workings of blockchain technology and the security measures that are in place to protect it.

Author

Oyeniyi Abiola Peace is a seasoned software and blockchain developer. With a degree in Telecommunication Science from the University of Ilorin and over five years experience in JavaScript, Python, PHP, and Solidity, he is no stranger to the tech industry. Peace currently works as the CTO at DFMLab and is a Community Moderator at Celo Blockchain. When he's not coding or teaching, he loves to read and spend time with family and friends.

Go back

· 11 min read

Introduction

The Denial of Service attack in Solidity is a comprehensive attack. Fundamentally speaking, it is an attack capable of preventing a contract from operating for a short period and in some cases, permanently. Essentially, the hacker tries to manipulate the contract to become unavailable to other users.

There are many ways that a hacker can achieve this but for this article, let us explore just one of these ways. We will see how Denial of Service (DoS) works when a contract does not accept Ether. We will also look at preventive measures to ensure our contracts don’t fall victim to this attack.

Explanation and Code Demo.

In this code demo, we have a contract called KingOfEther. This contract determines who the current king is by assigning the king's title to whoever sends it the most Ether. We use the popular King of The Ether ideology.

contract KingOfEther {
address public king;
uint public balance;

function claimThrone() external payable {
require(msg.value > balance, "Need to pay more to become the king");

(bool sent, ) = king.call{value: balance}("");
require(sent, "Failed to send Ether");

balance = msg.value;
king = msg.sender;
}
}

In the above code, we store the address of the current king in a state variable, called king. We also store the current balance in a state variable called balance.

//king state variable
address public king;

//balance state variable
uint public balance;

Below these variables, we have a function called claimThrone. Essentially, this function is used to assign a new king. In the function, we can see that it requires the value of the Ether being sent to be more than what the current balance in the contract is.

//requires value of Ether to be greater than current balance
require(msg.value > balance, "Need to pay more to become the king");

The function also sends back whatever was in the previous balance back to whoever was the former king. In an actual sense, the function requires that the balance be sent back before any other operation can be performed.

//send the balance back to the former king if msg.value > balance is true
(bool sent, ) = king.call{value: balance}("");
require(sent, "Failed to send Ether");

Once the above operation has been performed, the balance is set to the value of the Ether sent by the msg.sender, and msg.sender becomes the new king.

balance = msg.value;
king = msg.sender;

This is a simple contract, and any contract that sends it the most amount of Ether becomes the new king. Let us look at a simple real-life explanation to buttress the point further:

  • A man called James wants to become the king in this contract.
  • He sends 1 Ether, and since there was no previous king, James becomes the king of the contract.
  • If Bob decides that he wants to become the new king, he needs to send more than 1 Ether.
  • Bob understands this and sends 2 Ether.
  • Since the initial balance was 1 Ether, it is smaller than what Bob sent in.
  • The contract sees this and immediately sends 1 Ether back to James(the amount James sent in) and then makes Bob the new king.
  • The balance in the contract then becomes 2 Ether which was sent in by Bob.
  • If Dawn decides that she wants to take the position of king, she has to go through the same process.

Attacking Contract With DoS.

Now that we seem to better understand what the contract does, how can a Denial of Service attack be performed on this contract? Let us assume that Dawn does not just want the position of the king, but she wants to keep it forever.

To achieve this, she has to prevent the KingOfEther contract from accepting Ether from anybody who tries to take the throne from her. She has to deny the KingOfEther contract the ability to execute all its functions.

Programmatically speaking, Dawn has to render just two lines of code useless. Look at these lines below.

(bool sent, ) = king.call{value: balance}("");
require(sent, "Failed to send Ether");

After a closer look at the two lines of code, we can see that the first line makes use of the Call interface to send back the balance to whoever the previous king was. Dawn knows this, and she also knows that the new king is set only after the above operation has been successful. So when Dawn wants to claim the throne, she has to ensure that the above lines of code fail. How does she plan to do this? Let us look at the code she writes to claim the throne.

contract Dawn {

function attack(KingOfEther kingOfEther) public payable {
kingOfEther.claimThrone{value: msg.value}();
}
}

Dawn writes her contract and declares a function called attack(). This function takes in a single value which is the address of the KingOfEther contract. Since it needs to send Ether to the claimThrone() function in other to claim the throne, Dawn declares it as payable.

When the attack() function is called, it sends Ether to the KingOfEther contract. For this attack to work, Dawn has to make sure that she sends an amount higher than what the previous king sent.

Let us walk through what will happen with this contract in play:

  • Dawn decides that she wants the throne from Bob.
  • She sends 4 Ether to the KingOfEther contract with her contract, Dawn.
  • The KingOfEther contract assigns the throne to her.
  • Bob tries to take back the throne for himself.
  • Bob send 10 Ether to the KingOfEther contract.
  • Bob gets denied the chance to become king again.

What could have happened? Bob sent enough Ether for him to become the new king, but he isn’t. Could Dawn have done something that we overlooked when going through her code? That’s right, she did!

If we look properly, we will observe that the claimThrone() function uses the call interface when sending the balance back to whoever the dethroned king was.

(bool sent, ) = king.call{value: balance}("");
require(sent, "Failed to send Ether");

Dawn knows this, and so when she created her contract, she failed to include a fallback function. This means that when Bob sent 10 Ether, the claimThrone() function made a call to Dawn.attack(), and since there was no fallback function to handle this, the Dawn contract failed to accept Ether, and the operation to send Ether failed. Remember that the new king is only set after the operation to send Ether has been successful. Since the operation failed, a new king can never be set. This means that Dawn will remain king forever.

By doing this, Dawn has rendered the KingOfEther contract useless to anyone who will try to use it again. She did this simply by refusing to accept Ether.

Preventive Measures Against Denial of Service Attack.

So far, we have demonstrated how harmful the DoS attack can be. We have also seen how relatively easy it can be to perform the attack. We need to know how to prevent this attack from affecting our contract. Before fully grasping this, we should look at the **pull-over-push** design mechanism in Solidity.

Pull-Over-Push

In Solidity, the pull-over-push mechanism/design is a way to prevent risks associated with sending/transferring Ether from a contract to the user(s). This design intends to push the risks associated with transferring Ether from the contract to the user. In the pull-over-push design, three participants are involved. They are:

  • The initiator of the transfer i.e., the owner of the contract.
  • The smart contract
  • The receiver of the funds being transferred.

The pull-over-push design makes use of mapping. In this design, the user is made to withdraw (pull) a certain amount that would have been sent (push) to him under another circumstance. If there is any inaccuracy or security risk, it will have no impact on other transactions. For an extensive explanation of pull-over-push, visit here

Prevent Dawn From Taking Control Of The Contract.

Now that we understand the pull-over-push design let us see how we can use it to secure our KingOfEther contract. The simple strategy is to modify our contract to require a dethroned king to withdraw ether from the contract (pull) instead of having the contract send the ether to the dethroned king (push).

Since we’ll be modifying our code for dethroned kings to withdraw ether, we need to track how much has been sent. To do this, we create a mapping from address to uint and call it balances.

address public king;
uint public balance;
//keeping track of how much is being sent
mapping(address => uint) public balances;

Next, we should remove the code that sends the ether. This is the code that Dawn was able to render useless. We will replace it with another code.

//remove this code
(bool sent, ) = king.call{value: balance}("");
require(sent, "Failed to send Ether");

//replace with this
balances[king] += balance;

With this change, we are simply saying that instead of having the contract, KingOfEther, send the balance to the dethroned king, the dethroned king will be able to withdraw the amount stored in balances[king] plus the amount we were initially going to send, i.e. balance.

After this, we will create a function called withdraw(). With this function, once a king becomes dethroned, he/she can call the withdraw() function and withdraw the money sent to the contract.

    function withdraw() public {
require(msg.sender != king, "Current king cannot withdraw");

uint amount = balances[msg.sender];
balances[msg.sender] = 0;

(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}

The withdraw() function requires that the current king will be unable to withdraw. Then the function stores the amount of ether that the msg.sender can withdraw in a variable called amount. Then we will set the amount that msg.sender can withdraw to zero.

        
//the king cannot withdraw from the balance
require(msg.sender != king, "Current king cannot withdraw");

//amount variable
uint amount = balances[msg.sender];

//set amount that can be withdrawn to zero
balances[msg.sender] = 0;

After this, we will send the ether to msg.sender.

        (bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");

**NOTE:** The reason why we have balances[msg.sender] = 0; before we send the ether is to prevent another attack called **reentrancy**.

Looking closely, the code we added to send the ether is also prone to a Denial of Service attack. However, we don’t have to worry about it at this point because the attack will only affect whoever the sender is i.e., the attacker. This means that everyone else can decide to claim the throne, but no one will be able to keep it forever.

View the full code, modified to prevent DoS below:

contract KingOfEther {
address public king;
uint public balance;
mapping(address => uint) public balances;

function claimThrone() external payable {
require(msg.value > balance, "Need to pay more to become the king");

balances[king] += balance;

balance = msg.value;
king = msg.sender;
}

function withdraw() public {
require(msg.sender != king, "Current king cannot withdraw");

uint amount = balances[msg.sender];
balances[msg.sender] = 0;

(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}

Conclusion.

In this article, we have successfully seen how a Denial of Service attack can come into play by refusing to accept ether. We have also covered the Pull-Over-Push design. Lastly, we have seen how to protect our contracts from getting attacked by this malicious attack.

Author

Oyeniyi Abiola Peace is a seasoned software and blockchain developer. With a degree in Telecommunication Science from the University of Ilorin and over five years experience in JavaScript, Python, PHP, and Solidity, he is no stranger to the tech industry. Peace currently works as the CTO at DFMLab and is a Community Moderator at Celo Blockchain. When he's not coding or teaching, he loves to read and spend time with family and friends.

Go back

· 8 min read

How To Transition From Web2 To Web3

The online world is continuously developing, expanding, fixing bugs, and adding new features to overcome limitations. Some advances, such as the rise of social media, have marked each generation of the Internet since the beginning. There will be a manageable break in continuity as Web2's shortcomings will likely be addressed by Web3 systems.

Web3 is an improved version of the web whose fundamental principles are founded on blockchain and cryptocurrency technologies which aim to return control to its users. It introduces many shifts, including the consolidation of content and digital space ownership into the hands of service providers with vested interests. The users of a Web3 network have complete authority over the network's data, resources, and even social dynamics.

However, the transition from Web2 to Web3 is something that only some are familiar with, as Web3 is still uncharted territory. That is why in this article, we explore the basic concept of what Web3 is, what its defining features are, and how to transition from Web2 to Web3 efficiently.

How do we Conceptualize Web3?

web 3.0 Image Source: istock

To conceptualize what he saw as an improved version of the web, Ethereum co-founder Gavin Wood came up with Web3. He defined its primary objective as restoring internet governance to its user community rather than private corporations. The Web3 system accomplishes this by utilizing blockchain and cryptocurrency technology to establish a system of ownership.

A comparison between the early and modern stages of the internet shows the striking improvements expected for web3 is explained below:

Web1 was a strictly informational online service, with no room for user participation or interaction. Web2, the version currently in use, is a read-only version of the web in which users can only collaborate and share information. In Web3, in addition to reading and writing, users will be expected to have some ownership over their online content.

In contrast to Web2, where centralized platform providers reap all the rewards, Web3's core goal is to create a decentralized internet that provides new applicability and benefits both developers and users. It is the next iteration of the internet, with the ability to make the internet's benefits available to everyone without requiring the authorization of a central authority.

Everyone will have access to Web3, and there will be a shift in how users conduct their online interactions and financial transactions. For instance, instead of dealing with a third-party intermediary, transactions will be handled directly between peers or between peers and a contracting party. Users will also have a voice in developing a service or product through voting and other forms of governance.

What are the Core Defining Features of Web3?

web2 vs web 3 Image Source: vitalflux

Web3 is defined by a set of technological and blockchain integrations that, when combined, create an upgraded version of the internet. Below are some of those features:

Decentralization: This idea is key to Web 3.0 architecture. In Web 2.0, information is typically stored in a single, central location, accessible only via a specific web address, and accessed by computers over the Hypertext Transfer Protocol (HTTP).

Since information will be located on Web 3.0 according to data type, data might be stored in several places at once, making the system decentralized. By doing so, power would shift from centralized institutions to the users. Users will be able to retain ownership of their data, which can be sold via decentralized data networks.

Trustless & Permissionless: Web 3.0 will be trustless (allowing members to engage directly without going through a trusted intermediary) and permissionless (anyone can participate without authorization from a governing body). As a result, Web3 will be characterized by decentralized applications (dApps), blockchain operations, and decentralized peer-to-peer networks.

A Semantic Web: The Web3 will involve an influx of technology that will lead to improved internet use. Through semantic search and analysis, the Semantic Web will enable web technologies to better generate, exchange, and connect content based on the actual meaning of words, rather than just keywords or numbers.

The Metaverse: The evolution to Web3 will also be featured with the acceptance and rise of the metaverse. Many in the IT field envision the metaverse as the next generation of the internet: a unified, social, resilient, and fully three-dimensional online environment where people can have unique and extraordinary experiences unavailable in the real world. That roughly captures the expectations from Web3.

These features show that information and content will become increasingly interconnected and pervasive in Web 3.0, with access provided through a variety of applications and a growing number of commonplace web-enabled devices. The question then becomes not if or when but how one should evolve to catch up with the Web.

How to Transition from Web2 to Web3

web2 to web 3 Image Source: leewayhertz

The transition from Web2 to Web3 is dependent on the right application of Web3 features for each phase of the transition. To shift seamlessly, you should be aware of the following options.

Understand Web3: Aside from understanding the core features of Web3, you must have a basic familiarity with Web3. To transition from one phase to the other, you'll need to understand the limitations of Web3 and find ways to make those limitations potential areas for improvement as defined by the core features.

For a Web3-based digital ecosystem to be successful, it must meet the needs of its users and solve Web2 problems. This necessitates a major shift in perception and operations, along with reconsidering how to adjust the current Web.

Given that not everyone will be required to make the same changes, it is crucial to make the shift smooth and construct a Web3 transition plan that suits your exact situation, and that will meet your future needs. Participating in a decentralized autonomous organization (DAO) can provide valuable insight into the inner workings of Web3.

Transition Slowly: Participating in this transition requires reassuring users that they won't notice a significant break in services due to your approach toward transitioning. This should be in addition to developing a user-friendly interface, enhancing the accessibility of Web3 components, and setting up learning methods that will ease users and operators into the new Web.

Do not Discard Web2: Every aspect of your online service, from the user interface to the data storage, must be hosted on-chain if a full transition to Web3 is to be realized. Because there is no requirement for outdated facilities, there will be fewer delays and less need for maintenance. This will produce a completely genuine Web3 experience while also dramatically enhancing performance.

However, you should note that an effective transitioning strategy will incorporate both Web2 and Web3 techniques. Moreover, Web3 and the metaverse will primarily alter marketing and communication roles, paving the way for new job titles, which means that transitioning from one to the other has to come from an already established and solid Web2 foundation.

What to Look out for when Transitioning from Web2 to Web3

Blockchain promises a wide variety of benefits and applications. However, there are still significant obstacles to overcome before this vision of Web3 can become a reality.

Partial Decentralization: Many so-called dApps are not fully decentralized since the front-end components of such services run on the cloud. Consequently, users must rely on Web2 infrastructure to access decentralized services, with the blockchain serving as a data transmission and storage gateway. Since the underlying consensus protocol lacks strong decentralization assumptions, blockchain ecosystems that should be intended as decentralized networks lose that distinction.

Lack of Interoperability: The persistent need for value transfer between blockchains is just one complication brought on by the rapid expansion of blockchain ecosystems. It is becoming more unlikely that a single blockchain will suit all of society's needs due to the trade-offs involved in decentralization, security, scalability, and prices.

Therefore, each blockchain ecosystem has become an isolated system with limited access. Users and developers of dApps who want to embrace easily and reap the benefits of numerous chains need to be improved by the blockchain's widespread inoperability. For the time being, the only way to gain access to multiple blockchain technologies is to address the interoperability issue.

Conclusion

For those who understand the potential of the blockchain revolution and realize that Web3 is the way of the future, Web2 can be a challenging place to transition from. Major technology firms from both the Web2 and Web3 spheres are trying to find seamless ways to ease into Web3.

An example is the Celo blockchain which allows people worldwide to conduct simple cryptocurrency transactions using their mobile phones. It is also essential that any attempt to rethink the Internet and transition from Web2 to Web3 be made with the users in mind rather than centralized corporations to meet its overarching standard of decentralization.

About the Author

Tomiwa Oladipo is a freelance content writer for the emerging Web3 industry, writing blogs and articles about blockchain, NFTs, DeFi, and cryptocurrencies. You can connect with me on LinkedIn.

References

Go back

· 8 min read

Despite claims of causing long-awaited changes in the financial system, several blockchain projects have failed to attain global mainstream adoption.

To bring blockchain technology to the masses, the Celo protocol has taken a WhatsApp-style approach, making it possible for anyone with a phone number to join this ground-breaking network.

It's fine if you've only recently heard of Celo. This article dives into what Celo is and why you should start using it immediately.

What is Celo?

Let's begin by taking a look at what Celo is.

Celo is a blockchain platform that allows people worldwide to conduct simple cryptocurrency transactions using their mobile phones. This opens up a whole new world of financial products that can be accessed via mobile devices. The protocol also lets you pay transaction/gas fees in any currency, which are then split evenly among the 100 validators on the network.

You may not understand the full value of what Celo intends to achieve until you discover that Blockchain alone consumes more than 1% of America's electrical production. Blockchain's general acceptance as a payment method has been limited by its high energy demand and technological difficulties for the average individual. But with Celo, only a phone number is needed as a public key for cryptocurrency transactions.

The Celo Platform, like other DeFi chain s, facilitates the development of smart contracts and can host DApps. This has marked it as a mobile-based Web 3 hub for many.

CELO, its native token, and the stablecoins cUSD, cEUR, and cREAL are its primary tokens.

Its native token, the CELO, was initially called CGLD after its first deployment. Even though the name has changed, there may still be references to Celo Gold in the code base. The token is the platform's native asset and the driving force behind the growth of the CELO ecosystem and the blockchain. As a CELO holder, you can vote in governance polls, stake with validators, and earn incentive tokens.

How Does Celo Work?

How does the CELO network, which uses the same blockchain technology as others, allow PayPal-like Web3 transactions through mobile phones while using so little power?

Well, this is because the Celo platform is driven by three primary systems:

The light clients, full nodes, and validator nodes

Light Clients

These main programs, such as Celo's mobile wallet, run on users' mobile phones and serve as transaction initiators.

Full Nodes

These act as intermediaries between validator nodes and light clients. They take in incoming transactions and relay them to validator nodes for verification. A portion of the fees goes to the full nodes servicing the light client.

Validator Nodes

These are the network's backbone, responsible for creating new blocks and verifying transactions or staking. At any given time, there are 100 validators, and the only requirement to join their ranks and earn voting rights is to stake 10,000 CELO. Each validator is given a slice of the block reward for every successful transaction and block creation.

(C:\Users\pc\docs\blog\27-12-22-Why-should-you-use-Celo-Blockchain\images\celo2.png)

What is Special About Celo?

Celo is, of course, a layer one chain, but with layer one and two chains appearing faster than we can blink, we have to wonder what makes Celo unique.

Which begs the question of why anyone would construct a smart contract on it. Why should I invest in it or use it to facilitate my transactions?

You will find six reasons below, describing why this is the next big thing.

First Network for the Smartphone Users

This is Celo's main feature and the reason for its popularity. Imagine a world where you can send money to a friend's phone number even if they haven't set up a wallet. By the time they do, they’ll meet it there.

That's because it employs a lightweight identification protocol that works even on mobile, letting users process validations and payments on their phones. Customers' phone numbers also serve as the transaction public keys.

On top of that, it has a mobile wallet (Valora) that syncs with the peer-to-peer network using the ultra-fast light client protocol.

Multiple Stablecoins

Any network that wants to compete in the blockchain industry on any level needs stablecoins that DeFi developers and potential investors can use on the platform.

The Celo development team planned for this and built it into the system from the beginning. After launching with cUSD as its primary stablecoin, Celo has since added cEUR and cREAL to the mix.

The three stablecoins are backed by Ethereum, the CELO token, and other coins such as BTC and USDT. Through a hybrid of the crypto collateralization model and seigniorage shares, Celo has created decentralized stablecoins that are easily corrected for any price fluctuations away from the peg.

The three stablecoins are compatible with both centralized and decentralized apps, with transaction times as fast as 5 seconds. To top it all off, the total gas fees for all transactions come to less than one cent. With stablecoins, you can easily avoid volatility. And because we consider stable tokens as a hybrid kind of algorithmic and collateralized , they are easier to keep pegged.

Gas Payment in Multiple Currencies

Like in every chain, gas fees are an integral part of the transaction and validation process. But unlike Ethereum, where gas fees must be paid in ether, Celo's gas fees can be paid in various cryptocurrencies in addition to the network's native CELO coin. You can pay using cUSD or one of several whitelisted ERC 20 tokens.

Customers can provide the address of the currency they want to use to pay for gas in the feeCurrency field of a transaction request. If you leave this field blank, the native CELO will be used automatically.

Intuitive Customer Interface

Celo adopted address-based encryption instead of regular cryptographic address payments. When you use Celo, all you need is a phone number, which is converted into a secure address, allowing you to send money to anyone on your contact list.

Anyone who can easily interact with Web2 Fintech company interfaces would have an easy time using the Celo Platform.

The Celo Platform also functions as an escrow where funds can be held until they are claimed. To illustrate, if you want to send money to someone who hasn't yet signed up for the service, you can send it to their phone number; once they've signed up, they'll be able to access the funds.

Easy to Scale

If you're a developer looking to avoid the same load problems you've encountered multiple times, there's no point in sticking to other chains. This is because Celo enables rapid block processing and validation, reducing transaction time and maintaining a strong network of validators.

To do this, it uses a pBFT-based PoS consensus protocol that can process transactions in a record five (5) seconds at high TPS rates and without the possibility of transaction reversal. Yet, the high TPS rates come without the baggage of a longer sync time and going through a series of chain sizes.

The robust security around the network prevents chain forks. Armed with this, your users will easily trust your project.

An Ecosystem for All

Celo is not just another blockchain. The network was designed to operate its applications and tokens, as well as host dApps and DeFi options powered by the CELO token.

But much more than that is its compatibility with the Ethereum Application Layer, which is responsible for many of its achievements.

This EVM compatibility ensures that working on CELO projects as a developer will be an Ethereum offshoot. Being a true layer 1 chain, your tools and signature schedule work perfectly well here.

Using its lightweight client, you can quickly move assets from other chains to Celo.

(C:\Users\pc\docs\blog\27-12-22-Why-should-you-use-Celo-Blockchain\images\celo3.jpg)

How Does Celo Keep Spammers From Harvesting Users' Contact Information?

This is a common question that people ask a lot about Celo. Celo knows that phone numbers are used as public keys, which puts its users at risk of being hacked by harvesters. As a result, Celo uses a one-way hash of the address instead of the address itself as a workaround, which they outline in their whitepaper. Adding pepper to a text before it is hashed to increase its entropy works like a charm and makes it more difficult to reverse the hash.

Conclusion

The Celo protocol is the social payment platform of the future for mobile phone users. With the built-in address-based encryption protocol, all a user needs is their phone number, which doubles as the protocol's public key.

Considering its social impact, ultra-lightweight transaction system, and low and multicurrency gas fees, it is no surprise that adoption will increase at all levels.

About the Author

Adefolalu Adegboyega is a freelance content writer specializing in creating blogs and articles about blockchain, DeFi, NFTs, and cryptocurrencies for the emerging Web3 sector.

References

Celo Whitepapers