What you will be building, see the and for more info. live demo GitHub repo Introduction The demand for Web3.0 solutions is at an all-time high, but there isn't enough material available to usher in the army of developers required to fill the job openings. To assist other web developers, I've created this tutorial to help you understand how to build a decentralized eCommerce platform that transacts Ethers. You will be able to do the following by the end of this tutorial: Build an eCommerce app. Integrate Web3.0 Payment solution. Incorporate Customer Chat functionality. Interact with a Database using Firebase v9. Code and Deploy a Solidity Smart Contract. Hookup Smart Contract with React App. Lot’s more. This is tutorial is PART-ONE of a two-part series, we will begin with developing the solidity smart contract. So If you are pumped for this build, then let’s get coding… Prerequisites For PART-ONE of this tutorial, you will need the following items to build along with me; installed on your machine. NodeJs installed. Yarn package manager installed. Truffle installed. Ganache installed. Git CLI And an agile determination to learn. Installing App Dependencies To save you the pain of installing one dependency after another, I’ve prepared you a . Clone and install the dependencies with the instructions below. And… don’t forget to star the project. starter kit on my git repo On your terminal, navigate to the location of your project and run the code below: # Make sure you have the above prerequisites installed already! git clone https://github.com/Daltonic/truffle-starter-kit.git freshers cd frehsers # Navigate to the new folder. yarn install # Installs all the dependencies. I recommend you use for the tutorial, it has everything you will need for coding. vs code If you’ve done that, you’re awesome, let’s proceed to the next step… Coding the Smart Contract Open the project in VS code and head on to the contracts directory, you will see an existing contract named . Migration.sol Create a new solidity contract named . Inside of the store, define the following structures. Store.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract Store { // All codes goes in here! } This is a typical structure of a solidity smart contract, let’s code this smart contract one step at a time. // Defining store variables address public immutable storeOwner; uint256 public storeAcc; string public storeName; uint256 public immutable feePercent; uint256 public storeSales; These are the variables our smart contract will use to perform store sales. The and variables are , once the smart contract is deployed it can no longer be changed in the course of the program. store owner fee percent immutable Variables with address type mean that they can only hold data types of wallet address. Whereas variables with or mean unsigned integers, they can be used to hold only positive numbers with or without decimals. uint uint256 // Tracking users number of sales mapping(address => uint256) public salesOf; The code above describes a solidity variable with a key-value type of association. It's similar to the python hash method in that it returns a value if the argument passed in the parameter finds a match. // Declaring Events within each sale event Sale( address indexed buyer, address indexed seller, uint256 amount, uint256 timestamp ); event Withdrawal( address indexed receiver, uint256 amount, uint256 timestamp ); Events are useful for storing the arguments passed into the smart contract on the blockchain network. It’s an essential ingredient for writing a professional smart contract. // Structuring the sales object struct SalesStruct { address buyer; address seller; uint256 amount; string purpose; uint256 timestamp; } SalesStruct[] sales; We're describing a structure for collecting sales data in the code above. We want to collect the buyer and seller's addresses, the number of ethers transacted, the purpose of the transaction, and the time the transaction was completed for each sale made through our smart contract. Solidity provides us with a struct method, which is the best practice for ensuring that these records are correctly entered. // Initializing the store constructor( string memory _storeName, address _storeOwner, uint256 _feePercent ) { storeName = _storeName; storeOwner = _storeOwner; feePercent = _feePercent; storeAcc = 0; } These are the information passed during the deployment of the smart contract. // Performing sales payment function payNow(address seller, string memory purpose) public payable returns (bool success) { // Validating payments require(msg.value > 0, "Ethers cannot be zero!"); require(msg.sender != storeOwner, "Sale Not allowed"); // Calculating up cost and fee uint256 fee = (msg.value / 100) * feePercent; uint256 cost = msg.value - fee; // Assigning sales and payment to store and product owner storeAcc += msg.value; storeSales += 1; salesOf[seller] += 1; // Cashing out to sales party withdrawMoneyTo(storeOwner, fee); withdrawMoneyTo(seller, cost); // Recording sales in smart contract sales.push( SalesStruct(msg.sender, seller, cost, purpose, block.timestamp) ); // Captures sales data on event emit Sale(msg.sender, seller, cost, block.timestamp); return true; } This function collects payment from a buyer and sends the seller of the sales and to the store owner as the fee for utilizing their platform. 90% 10% // Sends ethers to a specified address function _payTo(address _to, uint256 _amount) internal { (bool success1, ) = payable(_to).call{value: _amount}(""); require(success1); } This is an internal method for transferring ethers to a specified address, it works in conjunction with the withdrawal function. It can only be called by another function within our smart contract. // Performs ethers transfer function withdrawMoneyTo(address receiver, uint256 amount) internal returns (bool success) { require(storeAcc >= amount, "Insufficent Fund!"); _payTo(receiver, amount); storeAcc -= amount; // Captures transfer data on event emit Withdrawal(receiver, amount, block.timestamp); return true; } This is a function that performs the sending of money to a specified address. It makes sure that it checks for balances before carrying out the transaction. // Retreives all processed sales from smart contract function getAllSales() public view returns (SalesStruct[] memory) { return sales; } Lastly, this function returns an array of all the sales that have taken place on our smart contract. The full code looks like this… // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract Store { // Defining store variables address public immutable storeOwner; uint256 public storeAcc; string public storeName; uint256 public immutable feePercent; uint256 public storeSales; // Tracking users number of sales mapping(address => uint256) public salesOf; // Declaring Events within each sale event Sale( address indexed buyer, address indexed seller, uint256 amount, uint256 timestamp ); event Withdrawal( address indexed receiver, uint256 amount, uint256 timestamp ); // Structuring the sales object struct SalesStruct { address buyer; address seller; uint256 amount; string purpose; uint256 timestamp; } SalesStruct[] sales; // Initializing the store constructor( string memory _storeName, address _storeOwner, uint256 _feePercent ) { storeName = _storeName; storeOwner = _storeOwner; feePercent = _feePercent; storeAcc = 0; } // Performing sales payment function payNow(address seller, string memory purpose) public payable returns (bool success) { // Validating payments require(msg.value > 0, "Ethers cannot be zerro!"); require(msg.sender != storeOwner, "Sale Not allowed"); // Calculating up cost and fee uint256 fee = (msg.value / 100) * feePercent; uint256 cost = msg.value - fee; // Assigning sales and payment to store and product owner storeAcc += msg.value; storeSales += 1; salesOf[seller] += 1; // Cashing out to sales party withdrawMoneyTo(storeOwner, fee); withdrawMoneyTo(seller, cost); // Recording sales in smart contract sales.push( SalesStruct(msg.sender, seller, cost, purpose, block.timestamp) ); // Captures sales data on event emit Sale(msg.sender, seller, cost, block.timestamp); return true; } // Sends ethers to a specified address function _payTo(address _to, uint256 _amount) internal { (bool success1, ) = payable(_to).call{value: _amount}(""); require(success1); } // Performs ethers transfer function withdrawMoneyTo(address receiver, uint256 amount) internal returns (bool success) { require(storeAcc >= amount, "Insufficent Fund!"); _payTo(receiver, amount); storeAcc -= amount; // Captures transfer data on event emit Withdrawal(receiver, amount, block.timestamp); return true; } // Retreives all processed sales from smart contract function getAllSales() public view returns (SalesStruct[] memory) { return sales; } } Now that we are done with coding this smart contract, it's time to test it programmatically. Setting up the Migration Scripts Before we proceed with testing out the smart contract, let’s set up the migration script in the folder. migrations Head to the migrations folder and create a new file called . Paste the following codes inside the file. 2_deploy_contracts.js 2_deploy_contracts.js const Store = artifacts.require('Store') module.exports = async (deployer) => { const [_feeAccount] = await web3.eth.getAccounts() const _name = 'Fresher' const _feePercent = 10 await deployer.deploy( Store, _name, _feeAccount, _feePercent ) } This will be necessary when we start testing out the smart contract. Testing the Smart Contract Spin up Ganache and ensure that it’s live and accessible. Next, locate the folder and create a file called . test Store.test.js Paste the code snippet inside of it. const Store = artifacts.require('Store') require('chai').use(require('chai-as-promised')).should() const toWei = (num) => web3.utils.toWei(num.toString()) const fromWei = (num) => web3.utils.fromWei(num.toString()) contract('Store', ([storeOwner, buyer, seller]) => { const feePercent = 10 const storeName = 'Freshers' let store beforeEach(async () => { store = await Store.new(storeName, storeOwner, feePercent) }) describe('deployment', () => { it('confirms store name', async () => { const result = await store.storeName() result.should.equal(storeName) }) it('confirms store owner', async () => { const result = await store.storeOwner() result.should.equal(storeOwner) }) it('confirms sales fee', async () => { const result = await store.feePercent() result.toString().should.equal(feePercent.toString()) }) }) describe('Sales process', () => { const amount = fromWei(toWei(4)) const purpose = 'Sales on dippers' it('confirms increase in sales of store and seller', async () => { // Sales of seller before purchase let result = await store.salesOf(seller) result.toString().should.equal('0') // Sales of store before purchase result = await store.storeSales() result.toString().should.equal('0') // Perform sales await store.payNow(seller, purpose, { from: buyer, value: amount }) // Sales of after after purchase result = await store.salesOf(seller) result.toString().should.equal('1') // Sales of store after purchase result = await store.storeSales() result.toString().should.equal('1') }) }) }) The above test is designed to check that our smart contract can perform sales. An extra measure to ensure that your smart contract runs accordingly is to write a script that will interact with it. Let’s do that next. Interacting with the Smart Contract This is the best way to validate the functionalities of your smart contract. We want to write a script to simulate the sales process. Head to the and create a file called . Next, paste the following codes inside of it. scripts folder performSales.js const Store = artifacts.require("Store"); module.exports = async (callback) => { const [storeOwner, seller, buyer] = await web3.eth.getAccounts() const amount = web3.utils.toWei('4', 'ether') const purpose = 'Sales on dippers' const store = await Store.deployed() let buyerBal = await web3.eth.getBalance(buyer) let sellerBal = await web3.eth.getBalance(seller) let storeOwnerBal = await web3.eth.getBalance(storeOwner) console.log(`Initial balance of buyer | ${web3.utils.fromWei(buyerBal.toString(), 'ether')}`) console.log(`Initial balance of seller | ${web3.utils.fromWei(sellerBal.toString(), 'ether')}`) console.log(`Initial balance of storeOwner | ${web3.utils.fromWei(storeOwnerBal.toString(), 'ether')}`) console.log(`\n ${purpose} for ${web3.utils.fromWei(amount.toString(), 'ether')} ethers... \n`) await store.payNow(seller, purpose, {from: buyer, value: amount}) buyerBal = await web3.eth.getBalance(buyer) sellerBal = await web3.eth.getBalance(seller) storeOwnerBal = await web3.eth.getBalance(storeOwner) console.log(`Balance of buyer after sales | ${web3.utils.fromWei(buyerBal.toString(), 'ether')}`) console.log(`Balance of seller after sales | ${web3.utils.fromWei(sellerBal.toString(), 'ether')}`) console.log(`Balance of storeOwner after sales | ${web3.utils.fromWei(storeOwnerBal.toString(), 'ether')}`) callback() } Cool, after creating and pasting the codes above, run the following command on the terminal. Please make sure that your ganache is up and running. truffle migrate --reset You will observe the following result on your terminal. If you reached here, you’re awesome, let’s run the script by running this code on the terminal. perfomSales truffle exec scripts/performSales.js You should have something like this on your terminal… Fantastic, we can be happy that our smart contract is certified and fully functional. Let’s deploy it to the . rinkeby test net Deploying the Smart Contract To do this deployment, configure your truffle config file in the order below: require('dotenv').config() const HDWalletProvider = require('@truffle/hdwallet-provider') module.exports = { // Configure networks (Localhost, Kovan, etc.) networks: { development: { host: '127.0.0.1', port: 7545, network_id: '*', // Match any network id }, rinkeby: { provider: () => new HDWalletProvider(process.env.SECRET_KEY, process.env.ENDPOINT_URL), network_id: 4, // Rinkeby's id gas: 5500000, // Rinkeby has a lower block limit than mainnet confirmations: 2, // # of confs to wait between deployments. (default: 0) timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) skipDryRun: true, // Skip dry run before migrations? (default: false for public nets ) }, }, contracts_directory: './contracts/', contracts_build_directory: './src/shared/abis/', // Configure your compilers compilers: { solc: { version: '0.8.11', optimizer: { enabled: true, runs: 200, }, }, }, } Deploying to Alchemy At the moment, our smart contract can only run on our computer, and no one else can connect to it. We will use alchemy to make it available to everyone at no cost. Sign up with them now, or if you already have an account. log in When you log in, you will see the dashboard page, which allows you to create a new blockchain application. Click on the button and enter the information shown in the image below, making sure to include the network. Creating an Alchemy App CREATE APP Rinkeby test After you've created the app, you can view its information by clicking on the app's name or the view the details button. Copy the as shown in the image below by clicking on the button. WSS URL VIEW KEY Amazing, now proceed as shown in the images below to obtain your account. Please keep in mind that we are not using the regular account address, but rather the private key to that account. Rinkeby ENDPOINT_URL=<YOUR_ALCHEMY_WSS_URL> SECRET_KEY=<YOUR_METAMASK_SECRET_PHRASE> DEPLOYER_KEY=<YOUR_METAMASK_PRIVATE_KEY> Please do not use your real ; these keys must be kept secret. That's why we put them in the environment file and tell git to ignore them. Metamask details After you've entered the above keys into their respective variables, execute the commands below. truffle migrate --reset --network rinkeby As we can see below, your smart contract will be deployed on the Rinkeby test net. Wow, you've worked hard to get to this point. You've just finished deploying an eye-catching smart contract to the Ethereum blockchain network. We'll connect it to a ReactJs frontend in of this tutorial. PART-TWO Conclusion You've completed the first part of this tutorial; next, we'll learn how to connect the solidity smart contract we just deployed to our React frontend. You can see the finished version of this application , and you can also check out the , which you should star. live here git repo here I'll see you again in of this tutorial. PART-TWO About the Author Gospel Darlington kick-started his journey as a software engineer in 2016. Over the years, he has grown full-blown skills in JavaScript stacks such as React, ReactNative, VueJs, and more. He is currently freelancing, building apps for clients, writing technical tutorials teaching others how to do what he does. Gospel Darlington is open and available to hear from you. You can reach him on , , , or on his . LinkedIn Facebook Github website Also published here