visit
For this, I prefer Readme.md
on . But, in this tutorial, we attempt to guide in depth so that any sort of user can build their dApp with the ease of understanding the tutorial stepwise. We might propose you download the Frontend code from the GitHub repository, and add it to the appropriate directory. We'll cover everything from setting up the project to deploying smart contracts and creating an interactive Frontend with real-time features.
mkdir rsk-agri-marketplace
cd rsk-agri-marketplace
npm init -y
truffle init
This creates the basic structure: • contracts/
- Contains Solidity contracts • migrations/
- Deployment scripts • test/
- Tests for your contracts • truffle-config.js
- Truffle configuration file
npm install dotenv
REACT_APP_PINATA_API_KEY=Your API Key
REACT_APP_PINATA_SECRET_API_KEY=Secret API Key
MNEMONIC=12 words mnemonic key
RSK_TESTNET_URL=//public-node.testnet.rsk.co
REACT_APP_CONTRACT_ADDRESS=Contract Address
Please create the .env
file without extra spaces or character mismatches else you will face difficulties later. Please, remember this Step because you are updating the smart contract later. Get Pinata API from .
require('dotenv').config();
const HDWalletProvider = require('@truffle/hdwallet-provider');
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*",
},
rskTestnet: {
provider: () =>
new HDWalletProvider({
mnemonic: {
phrase: process.env.MNEMONIC,
},
providerOrUrl: `//public-node.testnet.rsk.co`,
chainId: 31, // RSK Testnet ID
pollingInterval: 15000,
}),
network_id: 31,
gas: 2500000,
gasPrice: 60000000,
confirmations: 2,
timeoutBlocks: 60000,
skipDryRun: true,
},
},
compilers: {
solc: {
version: "0.8.20",
},
},
db: {
enabled: false,
},
};
npm install @truffle/hdwallet-provider
npm install @openzeppelin/contracts
👉Create Marketplace.sol
in contracts/
directory:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
contract Marketplace is ReentrancyGuard, Pausable {
uint public productCount = 0;
struct Product {
uint id;
address payable seller;
string name;
string description;
string imageHash; // IPFS hash
uint price; // Price in tRBTC
bool active;
}
mapping(uint => Product) public products;
event ProductCreated(
uint id,
address seller,
string name,
string description,
string imageHash,
uint price,
bool active
);
event ProductPurchased(
uint id,
address seller,
address buyer,
uint price
);
event ProductRemoved(uint id, address seller);
function createProduct(
string memory _name,
string memory _description,
string memory _imageHash,
uint _price
) public whenNotPaused {
require(bytes(_name).length > 0, "Name is required");
require(_price > 0, "Price must be positive"); // Price is expected in tRBTC
productCount++;
products[productCount] = Product(
productCount,
payable(msg.sender),
_name,
_description,
_imageHash,
_price,
true
);
emit ProductCreated(
productCount,
msg.sender,
_name,
_description,
_imageHash,
_price,
true
);
}
function purchaseProducts(uint[] memory _ids) public payable nonReentrant whenNotPaused {
uint totalCost = 0;
for (uint i = 0; i < _ids.length; i++) {
Product storage _product = products[_ids[i]];
require(_product.id > 0 && _product.id <= productCount, "Invalid product ID");
require(_product.active, "Product is not active");
require(_product.seller != msg.sender, "Seller cannot buy their own product");
totalCost += _product.price;
}
require(msg.value >= totalCost, "Insufficient funds");
for (uint i = 0; i < _ids.length; i++) {
Product storage _product = products[_ids[i]];
(bool success, ) = _product.seller.call{value: _product.price}("");
require(success, "Transfer failed to the seller");
// Emit purchase event (product can be bought again)
emit ProductPurchased(
_product.id,
_product.seller,
msg.sender,
_product.price
);
}
}
function removeProduct(uint _id) public {
Product storage _product = products[_id];
require(_product.id > 0 && _product.id <= productCount, "Invalid product ID");
require(_product.seller == msg.sender, "Only the seller can remove the product");
_product.active = false; // Mark the product as inactive
emit ProductRemoved(_id, msg.sender);
}
function getProduct(uint _id) public view returns (Product memory) {
require(_id > 0 && _id <= productCount, "Invalid product ID");
Product memory product = products[_id];
require(product.active, "Product is not available");
return product;
}
function pause() public {
_pause();
}
function unpause() public {
_unpause();
}
}
👉Write Migration Script in migrations/2_deploy_contracts.js
const Marketplace = artifacts.require("Marketplace");
module.exports = function (deployer) {
deployer.deploy(Marketplace);
};
truffle compile
Run the following command in the terminal to deploy Marketplace.sol
to the Rootstock’s testnet.
truffle migrate --network rskTestnet
You will find Marketplace.json
file in \build\contracts\Marketplace.json
remember it, you will copy this file to another directory.
npx create-react-app client
cd client
npm install web3 bootstrap
👉Web3 Setup insrc/utils/Marketplace.json
Marketplace.json
ABI from your Truffle build/contracts
directory into the client/src/utils/
folder as mentioned in Step.
App.js
file. Download it from and place it in the appropriate directory as shown in Figure 1.
For real-time notifications, we will integrate a library-like react-toastify
. You can also use react-bootstrap
for progress bars.
Install React Toastify in the client
directory
npm install react-toastify
npm install axios
npm start