Blockchain-Based Supply Chain Tracker Solidity, JavaScript
👤 Sharing: AI
Okay, here's a basic example of a blockchain-based supply chain tracker using Solidity (for the smart contract) and JavaScript (for interacting with the contract and displaying data). This is a simplified demonstration and would need more features for a real-world application.
**Solidity Smart Contract (SupplyChain.sol):**
```solidity
pragma solidity ^0.8.0;
contract SupplyChain {
// Define a struct to represent an item's status at different points in the supply chain
struct Item {
string itemName;
string itemDescription;
string currentLocation;
uint256 timestamp;
address owner;
}
// Define a mapping to store the item data. The key will be a unique ID for the item.
mapping(uint256 => Item) public items;
// Keep track of the item IDs
uint256 public itemCount = 0;
// Event to log when an item is added or updated
event ItemAdded(uint256 itemId, string itemName, address owner);
event ItemUpdated(uint256 itemId, string currentLocation);
// Function to add a new item to the supply chain
function addItem(string memory _itemName, string memory _itemDescription, string memory _initialLocation) public {
itemCount++;
items[itemCount] = Item(_itemName, _itemDescription, _initialLocation, block.timestamp, msg.sender);
emit ItemAdded(itemCount, _itemName, msg.sender);
}
// Function to update the location of an item
function updateLocation(uint256 _itemId, string memory _newLocation) public {
require(_itemId > 0 && _itemId <= itemCount, "Invalid item ID");
require(items[_itemId].owner == msg.sender, "Only the owner can update the location");
items[_itemId].currentLocation = _newLocation;
items[_itemId].timestamp = block.timestamp;
emit ItemUpdated(_itemId, _newLocation);
}
// Function to get item details
function getItem(uint256 _itemId) public view returns (string memory, string memory, string memory, uint256, address) {
require(_itemId > 0 && _itemId <= itemCount, "Invalid item ID");
return (items[_itemId].itemName, items[_itemId].itemDescription, items[_itemId].currentLocation, items[_itemId].timestamp, items[_itemId].owner);
}
}
```
**Explanation of the Solidity Contract:**
1. **`pragma solidity ^0.8.0;`**: Specifies the Solidity compiler version to use.
2. **`contract SupplyChain { ... }`**: Defines the smart contract named `SupplyChain`.
3. **`struct Item { ... }`**: Defines a structure called `Item` to hold information about an item in the supply chain. It includes the item's name, description, current location, timestamp of the last update, and the address of the owner (the one who added or updated the item).
4. **`mapping(uint256 => Item) public items;`**: A mapping (like a dictionary or hash table) that stores `Item` structs. The key is a `uint256` (unsigned integer) representing the item's unique ID. The `public` keyword automatically creates a getter function for this mapping.
5. **`uint256 public itemCount = 0;`**: A counter to keep track of the number of items added to the supply chain. This also serves as the unique ID for each item.
6. **`event ItemAdded(uint256 itemId, string itemName, address owner);`** and **`event ItemUpdated(uint256 itemId, string currentLocation);`**: Events that are emitted when an item is added or its location is updated. Events are used to log activity on the blockchain and can be listened to by external applications (like your JavaScript code) to update the UI.
7. **`function addItem(string memory _itemName, string memory _itemDescription, string memory _initialLocation) public { ... }`**: This function allows adding a new item to the supply chain.
* It increments the `itemCount` to generate a new unique ID.
* It creates a new `Item` struct and populates it with the provided data. `msg.sender` represents the address of the person (or contract) calling the function.
* It emits the `ItemAdded` event.
8. **`function updateLocation(uint256 _itemId, string memory _newLocation) public { ... }`**: This function allows updating the current location of an item.
* `require(_itemId > 0 && _itemId <= itemCount, "Invalid item ID");`: This checks if the provided item ID is valid. If not, the transaction will revert.
* `require(items[_itemId].owner == msg.sender, "Only the owner can update the location");`: This checks if the caller is the owner of the item. Only the owner can update the item location.
* It updates the `currentLocation` and `timestamp` of the item.
* It emits the `ItemUpdated` event.
9. **`function getItem(uint256 _itemId) public view returns (string memory, string memory, string memory, uint256, address) { ... }`**: This function allows retrieving the details of an item based on its ID. The `view` keyword indicates that this function does not modify the contract's state (it only reads data).
**JavaScript (Frontend Interaction):**
This JavaScript code assumes you are using a library like `ethers.js` or `web3.js` to interact with the Ethereum blockchain. You'll also need a provider (like MetaMask) to connect to a blockchain network.
```html
<!DOCTYPE html>
<html>
<head>
<title>Supply Chain Tracker</title>
<script src="https://cdn.ethers.io/lib/ethers-5.4.umd.min.js" type="application/javascript"></script>
<style>
body { font-family: sans-serif; }
#itemDetails { margin-top: 20px; border: 1px solid #ccc; padding: 10px; display: none; }
</style>
</head>
<body>
<h1>Supply Chain Tracker</h1>
<h2>Add Item</h2>
<label for="itemName">Item Name:</label>
<input type="text" id="itemName"><br><br>
<label for="itemDescription">Item Description:</label>
<input type="text" id="itemDescription"><br><br>
<label for="initialLocation">Initial Location:</label>
<input type="text" id="initialLocation"><br><br>
<button onclick="addItem()">Add Item</button>
<hr>
<h2>Update Location</h2>
<label for="itemIdUpdate">Item ID:</label>
<input type="number" id="itemIdUpdate"><br><br>
<label for="newLocation">New Location:</label>
<input type="text" id="newLocation"><br><br>
<button onclick="updateLocation()">Update Location</button>
<hr>
<h2>Get Item Details</h2>
<label for="itemIdGet">Item ID:</label>
<input type="number" id="itemIdGet"><br><br>
<button onclick="getItemDetails()">Get Item Details</button>
<div id="itemDetails">
<h3>Item Details</h3>
<p><strong>Name:</strong> <span id="itemNameDisplay"></span></p>
<p><strong>Description:</strong> <span id="itemDescriptionDisplay"></span></p>
<p><strong>Current Location:</strong> <span id="currentLocationDisplay"></span></p>
<p><strong>Timestamp:</strong> <span id="timestampDisplay"></span></p>
<p><strong>Owner:</strong> <span id="ownerDisplay"></span></p>
</div>
<script>
// Replace with your contract address and ABI
const contractAddress = "YOUR_CONTRACT_ADDRESS"; // Replace with the deployed contract address
const contractABI = [
{
"inputs": [
{
"internalType": "string",
"name": "_itemName",
"type": "string"
},
{
"internalType": "string",
"name": "_itemDescription",
"type": "string"
},
{
"internalType": "string",
"name": "_initialLocation",
"type": "string"
}
],
"name": "addItem",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "itemId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "string",
"name": "itemName",
"type": "string"
},
{
"indexed": false,
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "ItemAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "itemId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "string",
"name": "currentLocation",
"type": "string"
}
],
"name": "ItemUpdated",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_itemId",
"type": "uint256"
}
],
"name": "getItem",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
},
{
"internalType": "string",
"name": "",
"type": "string"
},
{
"internalType": "string",
"name": "",
"type": "string"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_itemId",
"type": "uint256"
},
{
"internalType": "string",
"name": "_newLocation",
"type": "string"
}
],
"name": "updateLocation",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "items",
"outputs": [
{
"internalType": "string",
"name": "itemName",
"type": "string"
},
{
"internalType": "string",
"name": "itemDescription",
"type": "string"
},
{
"internalType": "string",
"name": "currentLocation",
"type": "string"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "itemCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
];
async function getContract() {
// Modern dapp browsers...
if (window.ethereum) {
try {
// Request account access if needed
await window.ethereum.request({ method: "eth_requestAccounts" });
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(contractAddress, contractABI, signer);
return contract;
} catch (error) {
console.error("User denied account access")
}
}
// Legacy dapp browsers...
else if (window.web3) {
// Use Mist/MetaMask's provider
const provider = new ethers.providers.Web3Provider(window.web3.currentProvider);
const signer = provider.getSigner();
const contract = new ethers.Contract(contractAddress, contractABI, signer);
return contract;
}
// If no injected web3 instance is detected, fallback to backup node
else {
console.log("Non-Ethereum browser detected. You should consider trying MetaMask!");
}
}
async function addItem() {
const itemName = document.getElementById("itemName").value;
const itemDescription = document.getElementById("itemDescription").value;
const initialLocation = document.getElementById("initialLocation").value;
try {
const contract = await getContract();
const transaction = await contract.addItem(itemName, itemDescription, initialLocation);
await transaction.wait(); // Wait for the transaction to be mined
alert("Item added successfully!");
} catch (error) {
console.error("Error adding item:", error);
alert("Failed to add item. See console for details.");
}
}
async function updateLocation() {
const itemId = document.getElementById("itemIdUpdate").value;
const newLocation = document.getElementById("newLocation").value;
try {
const contract = await getContract();
const transaction = await contract.updateLocation(itemId, newLocation);
await transaction.wait();
alert("Location updated successfully!");
} catch (error) {
console.error("Error updating location:", error);
alert("Failed to update location. See console for details.");
}
}
async function getItemDetails() {
const itemId = document.getElementById("itemIdGet").value;
try {
const contract = await getContract();
const [itemName, itemDescription, currentLocation, timestamp, owner] = await contract.getItem(itemId);
document.getElementById("itemNameDisplay").textContent = itemName;
document.getElementById("itemDescriptionDisplay").textContent = itemDescription;
document.getElementById("currentLocationDisplay").textContent = currentLocation;
document.getElementById("timestampDisplay").textContent = new Date(timestamp * 1000).toLocaleString(); // Convert timestamp to readable format
document.getElementById("ownerDisplay").textContent = owner;
document.getElementById("itemDetails").style.display = "block"; // Show the item details
} catch (error) {
console.error("Error getting item details:", error);
alert("Failed to get item details. Make sure the item ID is valid.");
document.getElementById("itemDetails").style.display = "none"; // Hide if error
}
}
</script>
</body>
</html>
```
**Explanation of the JavaScript Code:**
1. **HTML Structure:** Sets up a basic HTML page with input fields for adding items, updating locations, and retrieving item details. It also includes a `div` to display the retrieved item information.
2. **`ethers.js` Import:** Includes the `ethers.js` library from a CDN. You'll need to ensure this is included in your project.
3. **`contractAddress` and `contractABI`:**
* `contractAddress`: **Replace `"YOUR_CONTRACT_ADDRESS"` with the actual address of your deployed smart contract.** This is crucial for the JavaScript code to know which contract to interact with.
* `contractABI`: The Application Binary Interface (ABI) of your smart contract. This is a JSON array that describes the functions, events, and data structures of your contract. You can usually get this from your Solidity compiler (e.g., Remix). **Make sure this matches your deployed contract!**
4. **`getContract()`**: This asynchronous function handles connecting to the blockchain using `ethers.js` and returns a contract instance. It checks for the presence of `window.ethereum` (MetaMask or a similar provider) and uses it if available.
5. **`addItem()`**:
* Retrieves the item name, description, and initial location from the input fields.
* Calls the `addItem` function on the smart contract using `contract.addItem(...)`.
* `transaction.wait()`: Waits for the transaction to be mined (confirmed) on the blockchain.
* Displays an alert to the user indicating success or failure.
6. **`updateLocation()`**:
* Retrieves the item ID and the new location from the input fields.
* Calls the `updateLocation` function on the smart contract.
* Waits for the transaction to be mined.
* Displays an alert.
7. **`getItemDetails()`**:
* Retrieves the item ID from the input field.
* Calls the `getItem` function on the smart contract.
* Extracts the item details from the returned values.
* Updates the content of the HTML elements within the `#itemDetails` div with the retrieved data.
* Converts the timestamp (which is in Unix epoch seconds) to a human-readable date and time using `new Date(timestamp * 1000).toLocaleString()`.
* Shows the `#itemDetails` div.
8. **Error Handling:** Each function includes `try...catch` blocks to handle potential errors during contract interaction. Error messages are logged to the console and displayed to the user.
**How to Use This Example:**
1. **Set up a Development Environment:**
* **Install Node.js and npm:** You'll need these for managing JavaScript dependencies.
* **Install MetaMask (or a similar Ethereum wallet):** This is a browser extension that allows you to interact with decentralized applications (dApps).
2. **Compile and Deploy the Solidity Contract:**
* Use Remix (an online Solidity IDE) or a local development environment like Truffle or Hardhat to compile the `SupplyChain.sol` contract.
* Deploy the compiled contract to a blockchain (e.g., a local Ganache instance, a testnet like Rinkeby, or the main Ethereum network).
* **Get the contract address:** After deploying, you'll get the address of your deployed contract. This is essential for the JavaScript code.
3. **Set up the JavaScript Project:**
* Create an HTML file (e.g., `index.html`) and copy the HTML/JavaScript code into it.
* **Replace `"YOUR_CONTRACT_ADDRESS"` with the actual address of your deployed contract.**
* **Make sure the `contractABI` matches the ABI of your deployed contract.**
* Open the `index.html` file in your browser.
4. **Interact with the DApp:**
* MetaMask will prompt you to connect to the website and authorize transactions.
* Use the input fields and buttons in the HTML to add items, update locations, and retrieve item details.
**Important Considerations and Further Improvements:**
* **Security:** This is a very basic example and is **not secure for production use.** You should consider:
* **Access Control:** Implement more robust access control mechanisms. Consider roles (e.g., manufacturer, distributor, retailer) and only allow specific roles to perform certain actions.
* **Input Validation:** Thoroughly validate all input data to prevent vulnerabilities like injection attacks.
* **Reentrancy Protection:** Be aware of reentrancy vulnerabilities, especially if your contract interacts with other contracts.
* **Gas Optimization:** Optimize your Solidity code to reduce gas costs.
* **Data Storage:** Storing large amounts of data directly on the blockchain can be expensive. Consider using off-chain storage solutions like IPFS or centralized databases for some data, and only store hashes or references on the blockchain.
* **User Interface:** Create a more user-friendly and visually appealing user interface.
* **Error Handling:** Implement more robust error handling and provide informative messages to the user.
* **Testing:** Write thorough unit tests for your Solidity contract to ensure its functionality and security.
* **Scalability:** Blockchain scalability is a challenge. Consider using Layer-2 scaling solutions if you need to handle a large number of transactions.
* **Real-World Data:** Integrate with real-world data sources (e.g., sensors, GPS devices, IoT devices) to automatically update location and other information.
* **Immutability vs. Flexibility:** Understand the trade-offs between immutability (one of the core benefits of blockchain) and the need to potentially update data or logic in the future. Consider using upgradeable contract patterns.
* **Permissions:** Design a robust permissioning system to control who can add, update, and view information in the supply chain.
This provides a starting point. Building a production-ready supply chain tracking system with blockchain requires careful planning, security considerations, and a deep understanding of blockchain technology. Remember to thoroughly test your code and consult with security experts before deploying to a live environment.
👁️ Viewed: 8
Comments