Skip to main content

28 posts tagged with "celo"

View All Tags
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

· 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

· 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

Go back

· 20 min read
Isaac Jesse

header

Introduction

The backbone of most web3 projects, if not all, is a well thought-out smart contract architecture that handles the sensitive aspect of the application such as financial transactions, health record of patients in a hospital, game logic and so on. Being a Celo developer, it is highly necessary to have a good engineering mindset when writing smart contracts to avoid costly mistakes that can put you at the mercy of the bad actors.

Prerequisites​

We are going to examine some of the best practices, approaches and good standards for writing smart contracts that are targeted at EVM-compatible networks such as Celo. This tutorial is best suited for anyone with:

  • At least foundational knowledge of smart contract development.
  • Experience with other programming languages like C++ or Python will be an added advantage.

Requirements​

We will leverage the following technologies and tools. Be sure to follow the steps accordingly.

  • VSCode
  • Nodejs >=v14.0.0
  • Hadhat.

Note: We will not cover testing in this tutorial. For testing, please refer to this tutorial

Contents

Some of the smart contract best practices we will discuss are as follows:

  • Avoid floating pragma.
  • Modular approach.
  • Circuit breaker.
  • External contract interaction.
  • Upgradability.
  • Check, effect, interaction.
  • Adopt libraries where possible.
  • Use Speed breaks where necessary.
  • Proper use of assert(), require() and revert() functions.
  • Use modifiers properly.
  • Integer division.
  • Fallback/receive function.
  • Explicit labeling of function visibility.
  • Use interface type for accepting contract addresses.
  • Use standard interface (S).

General consideration

In programming, it is assumed there is no software that is perfect. As a web3 developer, you should try to be ahead of the bad players. That is, finding, identifying and fixing loopholes in your code before others find it. Without further ado, let’s get started.

We will write fresh match-making contracts that connect and manage the processes of marriage between two opposite sexes. It features the following subjects:

  • HusbandToBe: Will have the following properties.

    • Inlaw.
    • Wife
    • Profile: (type struct)
    age
    isMale
    Bank - e.g Cash in bank balance etc
    Property : Owned tangible assets.
    Religion: What they believe in.
    Nature: Whether they are drunkard or otherwise.
    Status: Type "Enum" showing their status as single or taken or married.
  • WifeToBe: This contract will have similar properties such as:

  • Parents: Type address.

  • Husband: address (initially set to address(0)

  • isReady: Boolean - Whether the woman is ready for marriage or not.

  • Acceptance: bytes32.

  • Criteria: struct - (Personal preference of the woman about the kind of man she wants).

  • Status: Type "Enum" showing her status whether single, taken or married.

  • Inlaw: (In this case, the wife’s parents, since in Africa, parents give their daughters to the men).

  • Bride price: Type uint256

  • Bank: address - Account to receive the bride price.

  • Wealth (Husband’s possessions).

Both parties have common attributes such as operating joint accounts together etc.

Note: This is only for tutorial purposes. It may not necessarily display the exact practice. Clone this repository, then navigate to the project folder and install the dependencies.

git clone https://github.com/bobeu/advanced-hardhat-for-celo-dev.git
cd advanced-hardhat-for-celo-dev
yarn install

After that, navigate into the contracts folder and make a new folder

cd contracts
mkdir match-making

You should have a file structure that looks like this "contracts/match-making".

Avoid floating pragma

Often, many solidity developers ignorantly or unknowingly declare a list of compiler versions (solc) in contract files using the caret symbol "^" or a range of compiler versions with ">=". Declaring a version like this "pragma solidity ^0.8.9;" tells the compiler to make a decision for you by simply selecting a version starting from 0.8.9 but not including 0.9.0. If there is no version specified in the hardhatconfig, then it defaults to the version that was preinstalled during project setup. If you declare a list or range of versions with a version declared in the config file, the compiler resolves to using a version which is specified in the configuration file. Although external libraries like the openzeppelin’s mostly adopt the float style, that's acceptable since you will mostly inherit them as they serve as base contracts and not the final deployable contracts.

The best practice is to always specify a single compiler version at the top of your contract file. Always lock your pragma to one specific tested compiler version so that your contracts are not deployed using versions that introduce undiscovered bugs.

Overview of example contracts

Before a marriage takes place, there has to be two parties who are opposite sex expressing love to each other. So create a file under the "match-making" folder named "WifeToBe.sol", then paste the following code.

 pragma solidity 0.8.9;

Modular approach

Sometimes, one may need to write more code than expected. Having code with fewer lines may be easily maintained, but the case is different with lines that run into hundreds to thousands. Breaking your code into smaller modules i.e. sub-contracts makes it easy to detect and fix bugs quickly. Also, modularity improves readability and many more advantages. In our "match-making" contract, each of the subjects will be represented by a standalone deployable contract and they can interact by interfacing with one another. Now, create contract files representing each of the subjects. Since they will have to interact with one another, then it makes sense to have a separate folder for interfaces such as a husband-to-be proposing to a lady of his choice, to the lady, acceptance is optional. Such action should forward a response to the intended party. Populate each of the files with their respective code here

  • contracts
    • match-making
      • interfaces
        • Common.sol
        • IHusbandToBe.sol
        • IWifeTobe.sol
        • IParent.sol
      • wealth
        • CashInBank.sol
        • Properties.sol
      • HusbandToBe.sol
      • WifeToBe.sol
      • Parent.sol

Our contract code size is a bit bigger and we have successfully broken them down to implement a modular style.

Circuit breaker

  • This acts as a stopper or a switch that breaks executions.

  • In the event a bug is detected in your contract or a potential vulnerability has adversely affected the dApp, you need a way to mitigate or prevent a huge loss from further happening. Halting contract execution may be very effective. Usually, this would be a function that when invoked, would simultaneously prevent execution of other functions.

  • The openzeppelin "Pausable" module is an example of a circuit breaker. When your contract inherits from it, you will need to activate it by overriding the "_pause()" and "_unpause()" functions so that only authorized accounts e.g the "owner" can call it. In our case, we will need each of the subjects to be able to have some level of control over their respective profiles soon as they subscribed in order to prevent reserved functions from being invoked by anyone. By doing so, we will have a reasonable approval mechanism among the parties that regard and respect each party’s opinion such as the WifeToBe can express her personal feelings even while the parents feel certain things are right for her.

  • In Africa, we’ve had countless cases where parents force their daughters to marry men they do not wish to marry. Having such a tamper-proof system as the Celo blockchain to balance, checkmate preferences and respect opinions would go a long way to make the world a better place. We are going to leverage the openzeppelin’s "security" and "access" modules to implement a circuit breaker that pauses the contract when invoked by the authorized account i.e. "owner". Copy/paste the following code at the top of each standalone deployable contract file.

Your files should look like these:

HusbandToBe.sol

image

WifeToBe.sol

image

Inlaw.sol renamed to Parent.sol

image

Bank.sol

image

The openzeppelin library makes available for us two internal functions to activate the breaker. They hold so much power that care must be taken who calls them.

  • pause()
  • unpause()

Paste the following code at the bottom of each of the 4 files.

   // …. 
function pause() public onlyOwner {
_pause();
}

function unpause() public onlyOwner {
_unpause();
}

We have successfully made the functions public and only authorized owner account can call them but at this point, nothing happens when they are invoked. To effect the breaker, we need invoke it on relevant functions. From the "Pausable.sol", there are two utilities that are capable of modifying functions where they are present. In our case, we only need the latter.

  • whenPaused
  • whenNotPaused

In "WifeToBe.sol", invoke the "whenNotPaused" modifier on :

  • meetYourWife()
  • tryPropose()
  • setMarriageStatus()

Example:

image

Parent.sol

  • getMarriageApproval()

When "pause()" is invoked, all functions with the "whenNotPaused" modifier will fail.

External contract interaction/External call

In solidity, contracts can be configured as standalone or dependent on other contracts. In either context, they can be made to interact with other contracts either in a high-level or low-level way. Each of these methods have their pros and cons, and the modus operandi is always to consider safety first whichever best fits your need.

High level calls are proven to be more secure compared with low-level interactions since much control is not given to the message receiver unlike its counterpart where a call from contract to another could change the flow. It is always advisable to consider a low-level call last unless you know what you’re doing. In our case, we could use low-level interaction since we own the code and can always attest to its genuineness.

A good practice is to always tag or name that contracts we do not trust as "unsafe" when making external call. Another thing to note is that low-level functions : ".call()", ".staticcall()" and ".delegatecall()" do not throw an exception when the call stack is depleted, instead, they return false. This is very unsafe and so it is mostly discouraged.

image

On Line 78, we perform a high-level callback into whoever is calling to read their profile. In this case, we expect that the caller should be a contract of type "HusbandToBe". In our case, the "_msgSender()" is a trusted party so we do not have to worry about malicious code hijacking control flow. The flow terminates as soon as the execution of "getProfile()" completes, and the result is tracked in the "_p" variable (user-defined type from an elementary type ‘struct’). Assume we are using a low-level call, we could something like:

 bytes memory _calldata = abi.encodeWithSignature("getProfile()");
(bool done, bytes memory returndata) = _msgSender().call(_calldata);

If "_msgSender()" is a bad actor, they could execute malicious code that might cause serious damages to the calling contract. As much as possible, avoid low-level calls especially with in-line assembly except if you know what you’re doing. And if you need to make an external call, Avoid state changes after the call. Avoid delegating to code you do not trust. Below is an example how "delegatecall()" can cause loss of funds and the contract destroyed.

 contract BadActor {
function performActionOnMyBehalf() external {
selfdestruct(0);
}
}

contract Enquirer {
function getProfile(address proxy) public returns(string memory) {
// Unsafe call
proxy.delegatecall(bytes4(keccak256("performActionOnMyBehalf()")))
}
}

The potential danger here is allowing users to supply an argument to the "getProfile" function. This should be discouraged.

Let’s fix an issue in the WifeToBe.sol contract.

Before

image

After

image

Upgradability

Writing 100% secure smart contracts is nearly impossible especially with complex code. The immutability attribute of blockchain is one kind that makes it stand out from other technologies. What if a potential bug that could lead to loss of millions of dollars is detected? Does it mean we will have no way to fix it? Fortunately, overtime, the developer communities have developed strategies to fix issues in contracts through smart contract upgrade which comes at a cost. However, while some upgrade methods do not alter code already deployed to the blockchain but provide subtle ways to deploy several versions of the same software while retaining users’ trust and confidence, others may require a complete software upgrade with a new deployment address each time upgrade is performed. From description, the disadvantages are not far fetched. Smart contract upgrade is a wide topic on its own which is not covered in this tutorial. To learn more, please refer to these link: How to create upgradeable smart contract on Celo

Check, effect, interaction

This largely depends on the design of the code and what it tends to achieve. But the concept of check, effect pattern means you make the necessary check, effect necessary changes to state variable (s) before initiating external call. Example is determining if an user is entitled to receive funds in a contract. Consider the following code:


1 // SPDX-License-Identifier: MIT
2
3 pragma solidity 0.8.9;
4
5 contract Reward {
6 mapping(address=>uint256) public rewardBalances;
7
8 function claimReward() external returns(bool) {
9 uint bal = rewardBalances[msg.sender];
10 require(bal > 0, "No reward");
11 (bool s, ) = msg.sender.call{value: bal}("");
12 require(s, "Failed");
13 rewardBalances[msg.sender] -= bal;
14 }
15 }
16
17
18 contract BadActor {
19 Reward reward;
20 uint receiveCounter;
21
22 constructor (address rewardContract) {
23 reward = rewardContract;
24 }
25
26 receive() external payable {
27 receiveCounter ++;
28 drainBalance();
29 }
30
31 function drainBalance() public {
32 reward.claimReward();
33 }
34 }

The essence of the "claimReward" function is to send a reward to the caller if they have balances registered in their favor. Unfortunately, the bad actor is one of the beneficiaries who had submitted a contract address as sole beneficiary for the reward.

In the bad actor’s contract, he had written a function called "drainBalance" which calls back into "Reward.claimReward()" even before line 12 is reached. Here is how the flow goes:

  • The attacker invokes "drainBalance()" which then calls "Reward.claimReward()".
  • Reward extracts the caller’s balance, checks if the balance is greater than the minimum withdrawal amount.
  • It sends payment to the BadActor by calling the "receive()" function which then invokes "drainBalance" again. The circle is restarted while waiting for line 11 to finish execution. The balance of the Bad actor contract in "Reward" will always remain the same while it withdraws the same amount over and over until the balance in the Reward contract is completely drained.

An effective solution is using the check, effect, interaction pattern or the "@openzeppelin/contracts/security/ReentrancyGuard.sol" module.

1    // SPDX-License-Identifier: MIT
2
3 pragma solidity 0.8.9;
4
5 contract Reward {
6 mapping(address=>uint256) public rewardBalances;
7
8 function claimReward() external returns(bool) {
9 // check
9 uint bal = rewardBalances[msg.sender];
10 require(bal > 0, "No reward");
11
12 // effect
13 rewardBalances[msg.sender] -= bal;
14
15 // interaction
16 (bool s, ) = msg.sender.call{value: bal}("");
17 require(s, "Failed");
18
19 }
20 }

In "Bank.sol", we implement this pattern. image

Adopt libraries where possible

While writing your own code is encouraged, it's a good practice leveraging battle-tested code such as the openzeppelin library. Even at that, merely inheriting from the library is not enough, you should have full understanding of any of the contract modules you are using as base contract. From the image below, we import a few modules from the openzeppelin library.

image

Use speed break where necessary

Using method (s) that delays action for a set period of time before execution is finalized can be very helpful. It is useful in situations where the unplanned happens so there is enough time to react and/or recover loss (es) that should have occurred. For example where a substantial amount of funds is involved, you could set a waiting period of say 14 days before withdrawal is effected so there is room for action to be taken in event of an authorized action being performed. Below on line 73, we implemented a speed break.

image

Proper use of assert(), require() and revert()

Use "assert()" function only for internal conditional test and check invariants such as comparing the balance in a contract after a transaction has been effected. So this means that a properly effected transaction should never reach the assert code. The "assert()" statement should never be used to validate input.

The "require()" function should be used to validate conditions that must evaluate to true such as checking inputs from users. For example, if you’re expecting users to supply a number between 1 and 255, you should either use the "require" function or an "if" statement with "revert" function. Lines 101, 102 and many other places show how we implement "require()" and "revert()".

image

Use modifiers properly

Modifiers work similar to functions except that they don’t have visibility and the code inside modifiers are checked and executed before proceeding to the function body. Be careful when making an external call inside a modifier as it can lead to reentrancy attack. A proper use of modifier is to make it run a condition check that should have been duplicated in many places but where you have one conditional check, you should use either "require()" or "revert()" statement. The essence of this is to make your code more readable and avoid redundancy.

image

"isPartnerAOrB"** function modifier contains code used in almost all the functions, so it makes a lot of sense to have them in a modifier and simply reference them by a variable "isPartnerAOrB".

Integer division

Solidity does not provide a proper way to account for remainder when making integer division e.g "x = 9 / 4". All integer divisions always round down to the nearest integer and the remainder is lost. This is not ideal especially when dealing with payment split or computation that has to do with funds. If you need to achieve precision, you should consider using a multiplier but be mindful that any number you use as a multiplier must be accounted for in the future when dealing with the result. Alternatively, you could switch to using percentage if you can quantify the denominator in percentage. Use a multiplier to avoid rounding to zero.

image

Fallback/receive function

In solidity, fallback function as the name implies is similar to an escape function which is reserved for an event where the smart contract is called with an empty data or a function that does not exist in the contract is invoked. Fallback is used in version of solidity before 0.6.x in the following scenarios: Ether is sent to a contract with no data. Data is sent to a contract but no match for function in the data.

Sending ether to a contract without data via ".transfer()" or ".send()" triggers the contract’s fallback function. To receive ether, fallback must be payable. The syntax is often written as:

 fallback () external payable { }

Fallback function also serves a good use case in implementing upgradable contracts.

Example:

 pragma solidity 0. 5.4;

contract DelegateToProxy {
// contract address where the logic resides
address internal implemetationAddress;

function ( ) external payable {
// Retrieve the implementation address
address impl = implemetationAddress;

assembly {
calldatacopy(0, 0, calldatasize( ))
let result := delegatecall(gas( ), impl, 0, calldatasize( ), 0, 0)
returndatacopy(0, 0, returndatasize( ))

Switch result
case 0 { revert(0, returndatasize( )) }
default { return(0, returndatasize( )) }
}
}
}

Warning! This is for example purpose, do not use in production

From version 0.6.x an improvement was released that splitted the fallback function into two separate functions:

  • fallback() external payable { }
  • receive external payable { }

Using payable with "fallback()" is now optional, and the function is used when no other function in the contract matches the call data. It always receives data and it is declared without the "function" keyword. If you intend to only use the fallback function for logging received Ether, you should always check if the call contains any data or not.

 // …
fallback() external payable {
require(msg.data.length = = 0, "");
emit DepositConfirmed(msg.sender);
}

The receive function should be used where call data is empty and any value is sent via ".send()" or ".transfer()". A contract cannot have more than one "receive" function, and it is declared without the "function" keyword with its visibility marked as "external".

In our contracts, we also implement the receive function. As an exercise, try to implement a fallback function in each of the contracts that ensures a call to it does not include any data.

image

Explicit labeling of function visibility

You should always endeavor to explicitly label function visibility including state variables.

Different Kinds of visibility in solidity are :

  • external
  • public
  • internal
  • private

Facts about visibility in solidity

  • You cannot label state variables as private.

  • Explicit labeling corrects assumptions about who can call certain functions or access state variables.

  • Functions in the interface are always labeled "external".

  • Functions or variables marked as public can be accessed by anyone.

  • External functions cannot be called in internal functions.

  • Private variables and functions are not visible to other contracts but only contracts where they’re defined.

  • External functions may be more efficient when they receive large arrays of data.

  • Internal functions and state variables are only available internally without "this" keyword.

image

Use interface type for accepting contract addresses

When accepting a contract addresses arguments to functions, it is advisable to use the interface type rather than accepting an address type so as to enable the compiler guarantee type safety throughout the input lifecycle.

Example:

 contract Somecontract {
string openLetter = "LETTER";

function verify(string memory letter) external returns(bool) {
return letter = = openLetter;
}
}

contract Verifier {
function checkCorrectness(Somecontract addr, string memory _letter) public
{
addr.verify(_letter);
}
}

image

On line 84 we accept input of type contract for the proposer and proposedTo arguments.

Use standard interface (S)

If you’re creating token (s) for your project, ensure they follow acceptable and stable token standard by implementing corresponding token interface such as EIP20 or ERC20, EIP721

Example:

image

In the code above, we create a new ERC20 compatible token by inheriting the openzeppelin ERC20 module which already has the interface implemented.

Let’s compile the code. Run :

 npx hardhat compile

image

Deploy locally.

 npx hardhat run scripts/deploy.js

image

Deploying to Celo’s testnet (Alfajores)

 npx hardhat run scripts/deploy.js – testnet alfajores

image

Conclusion​

We learned some of the best ways to write smart contracts even though there are a couple of them we might not have talked about but you’ll mostly need the ones we discussed.

Next Steps​

The example we adopted in this tutorial is a real-world case. It can be extended to make meaningful project. As an exercise, practice what you have learned by improving the contracts. Firstly, deploy locally, then to Celo testnet. Share your deployment details with us on Celo’s discord server. Happy reading.

About the Author​

Isaac Jesse, aka Bobelr is a smart contract/Web3 developer. He has been in the field since 2018, worked as an ambassador with several projects like Algorand and so on. He has also contributed to Web3 projects.

References​

Complete tutorial source code

Go back

· 2 min read
Ewerton Lopes

header

Introdução

Realizar um deploy de um token ERC20 em Celo é fácil e rápido ao utilizar ferramentas de integração como Tatum.

Neste video mostraremos como você pode realizar um deploy do smart contract ERC20 na rede Alfajores (rede de teste) da Celo, mas também commo criar uma carteira, realizar transações, realizar uma recarga utilizando a Faucet.

Pré-requisitos

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

  • Tatum: Tatum é a maneira mais rápida de criar, testar e executar aplicativos blockchain. Eles oferecem APIs e SDKs para você implementar sua ideia usando blockchain
  • 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 uma conta no Tatum
  • Utilizar uma ferramenta para consulta de API como Postman

Tutorial

Confira no video como utilizar Tatum para realizar o deploy, consultas e transferências em Celo.

Conclusão

Parabéns! Você concluiu o tutorial de como realizar chamadas API usando Tatum e o blockchain da Celo 🎉 .

Próximos passos

Como próximos passos sugiro a você consultar a documentação de API da Tatum e realizar novas chamadas não contempladas neste tutorial.

Além disso, convido você a ver nosso próximo video que será explicando sobre a API para realizar deploy ERC721

Sobre o Autor

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

LinkedIn

Go back

· 2 min read
Ewerton Lopes

header

Introdução

Realizar um deploy de um token ERC721 em Celo é fácil e rápido ao utilizar ferramentas de integração como Tatum.

Neste video mostraremos como você pode realizar um deploy do smart contract ERC721 na rede Alfajores (rede de teste) da Celo, mas também commo criar uma carteira, realizar transações, realizar uma recarga utilizando a Faucet.

Pré-requisitos

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

  • Tatum: Tatum é a maneira mais rápida de criar, testar e executar aplicativos blockchain. Eles oferecem APIs e SDKs para você implementar sua ideia usando blockchain
  • 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 uma conta no Tatum.io
  • Utilizar uma ferramenta para consulta de API como Postman

Tutorial

Confira no video como utilizar Tatum para realizar o deploy e mint de um NFT em Celo.

Conclusão

Parabéns! Você concluiu o tutorial de como realizar chamadas API usando Tatum e o blockchain da Celo 🎉 .

Próximos passos

Como próximos passos sugiro a você consultar a documentação de API da Tatum e realizar novas chamadas não contempladas neste tutorial.

Além disso, convido você a ver nosso outro video que explica sobre a API Tatum para realizar deploy ERC20

Sobre o Autor

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

LinkedIn

Go back

· 6 min read

header

Introduction

We can't talk of blockchain solutions without mentioning Layer 1 and Layer 2 protocols and how they uniquely contribute to blockchain scalability. It is essential to know that Layer 1 primarily serves as the framework for Layer 2 and its solutions. Without Layer 1 scaling solutions, the consensus protocol, sharding, and the lightning network would not be possible.

What are Layer 1 Blockchains?

By definition, a protocol is Layer 1 when it completes transactions on its own blockchain. It's the term used for describing the underlying principal blockchain architecture. An example of a Layer 1 blockchain is the Celo blockchain which was branched from Go Ethereum (Geth) in 2017. It has, however, made some substantial adjustments, such as putting PoS and a distinctive address system in place. The Celo ecosystem, which comprises NFTs, payment methods, and DeFi, has over 100 million confirmed transactions. Also, anyone can create a public key using a phone number or email address. There is no need for specialist hardware since standard PCs can operate the blockchain with ease.

What are Layer 2 Blockchains?

Alternatively, a Layer 2 protocol built on top of an existing blockchain. The primary goal of this protocol is to address the major cryptocurrency networks' transaction speed and scaling issues. Without a doubt, Layer 1 and Layer 2 protocols uniquely determine blockchain scalability, which seems to improve as you progress from base structure Layer 1 to Layer 2. But why is scalability a complex blockchain problem that needs immediate solutions?

Why is Blockchain Scalability Important?

Scalability refers to a blockchain network's ability to expand due to a high influx of transactional throughput and future growth. It is crucial because it allows blockchain networks to compete against legacy centralized platforms which have rapid-fire transactional rates. Scalability trumps many of the inconsequential business needs as it is one of the top three qualities any promising tech start-up or business must present. Interestingly, scalability is one of the vital problems most blockchain technologies are geared to solve.

Blockchain Layer 1 vs. Layer 2 Scaling Solutions

Layers 1 and 2 are the primary means of achieving blockchain scalability. To compete with traditional payment processing systems, blockchain is consistently raising the bar. Blockchain requires a dependable and highly scalable product to have a chance. This network should be able to handle an increasing number of transactions, users, and data. Without this scalability, it would be nearly impossible to compete with or serve as many people as are willing to join the blockchain network. Incorporating scalability into the structure allows blockchain networks to outperform other well-known traditional payment systems and trademarks.

How Do Layer 1 Scaling Solutions Work?

Layer 1

Most Layer 1 protocols, as you might expect, have scaling issues. All transactions, in general, require validation by multiple nodes. This results in the formation of an ecosystem in which mining nodes compete to solve complex computational puzzles. Miners who succeed are rewarded in the network's native cryptocurrency. However, this consensus mechanism consumes a large amount of energy and creates a lag in transaction speed.

The Layer 1 solutions as we know them are

Consensus Protocol Changes

Layer 1 projects such as Ethereum have transitioned from consensus protocols such as Proof-of-Work (PoW) in favor of faster and less demanding consensus protocols such as Proof-of-Stake (PoS). Although Bitcoin continues to use the PoW, Ethereum has successfully migrated to a PoS.

Sharding

Sharding is a Layer-1 scalability method that most protocols use. Instead of having a network work on each and every transaction in sequence, sharding divides these transaction sets into small data sets known as "shards." These shards can then be processed in parallel by the network.

How do Layer 2 Scaling Solutions Work?

Image 2

We've established that the Layer 2 protocols use Layer 1 as a foundational base. The Layer 2 solutions include:

State Channels

A state channel is a two-way communication channel between participants that allows them to conduct interactions that would normally take place on the blockchain off the blockchain. This reduces waiting time because users no longer depend on third parties, such as a miner. A portion of the blockchain is sealed off via multi-signature or some sort of smart contract that the participants have pre-agreed upon. Participants can then communicate with one another without submitting anything to the miners. When the entire transaction set is completed, the channel's final state is added to the blockchain. State channel solutions include Bitcoin's Lightning Network and Ethereum's Raiden Network. They use Hashed Timelock Contracts (HTLCs) for state channel execution. They utilize the Hashed Timelock Contracts (HTLCs) for state channel execution.

Nested Blockchains

The Nested Blockchain offers multiple levels of blockchains on top of the main chain. These levels are connected to create a parent-child chain connection. The parent-child chain connection allows the parent chain to delegate tasks to individual child chains. Each child chain tirelessly executes these actions and provides results to its parent chain. This solution is quite efficient because it significantly reduces the load on the root chain and exponentially increases scalability.

Sidechains

Sidechains implement an independent consensus mechanism that is separate from the original chain. And they can be appropriately optimized for their speed and scalability. Its architecture is primarily maintaining overall security, confirms transaction collections and records, and resolves disputes. They are different from the state channels in some integral ways. First, sidechain transactions aren't exclusively private between participants as they are publicly recorded in the ledger. More interestingly, sidechain security breaches do not necessarily impact or affect the mainchain or other sidechains.

Limitations of Layer 1 and Layer 2 Scaling Solutions

Layer 1 provides an active and effective solution for wide-scale protocol improvements. But, it also comes with limitations like convincing validators before effecting a large-scale hard fork change with scalability being the "problem to solve," it is no easier with Layer 1. While scalability can be easily improved with Layer 2, making it somewhat better than Layer 1. However, Layer 2 isn't without its limitations, such as the security of the original blockchain being lost depending on the protocol. Lastly, the scalability trilemma coined by Ethereum creator Vitalik Buterin describes another challenge that blockchains face. He believes that protocols must balance scalability, security, and decentralization. These are somewhat contradictory because by focusing too much on two of the properties, the third will suffer.

What's Next after Layer 1 and Layer 2?

The future of Layer 1 and Layer 2 holds countless possibilities. There's quite the possibility that large chains like Celo and Ethereum continue to dominate due to their massive developer community and numerous user influence.

Closing Thoughts

The search for improved scalability is an unending one, and it has taken precedence since the inception of cryptocurrency. This search has created a central two-way tackle approach with Layer 1 and 2 solutions. Now we've provided you with the distinction that sets them apart and you can at least know what to expect from scalability.

About the Author

Joshua Obafemi

A web3 content writer. You can connect with me on Linkedin.

References