WEB3DEV

Cover image for Construindo um aplicativo descentralizado de rastreamento da cadeia de suprimentos com contratos inteligentes
Diogo Jorge
Diogo Jorge

Posted on

Construindo um aplicativo descentralizado de rastreamento da cadeia de suprimentos com contratos inteligentes

Image description

No atual cenário empresarial em rápida evolução, as cadeias de suprimentos (supply chains) desempenham um papel fundamental para garantir a circulação eficiente de bens e serviços em todo o mundo. No entanto, a complexidade inerente e o envolvimento de múltiplas partes interessadas nas cadeias de suprimentos conduzem frequentemente a desafios na manutenção da transparência, rastreabilidade e responsabilização. Apresentamos a tecnologia blockchain e os contratos inteligentes — uma solução revolucionária que tem o potencial de transformar o gerenciamento da cadeia de suprimentos. Neste artigo, nos aprofundaremos na criação de um aplicativo descentralizado de rastreamento da cadeia de suprimentos usando contratos inteligentes, abordando o problema da opacidade e ineficiência da cadeia de suprimentos.

_O problema: Transparência e rastreabilidade nas cadeias de suprimentos

A gestão tradicional da cadeia de suprimentos enfrenta vários problemas críticos. A falta de transparência nas diferentes fases da cadeia de abastecimento pode levar à falsificação de produtos, atrasos e até violações dos direitos humanos. Acompanhar o percurso de um artigo desde o fabricante até ao consumidor é uma tarefa difícil, muitas vezes dependente de intermediários cujos interesses podem não estar alinhados com a garantia da transparência.

Além disso, a natureza centralizada das bases de dados da cadeia de abastecimento torna-as suscetíveis à adulteração de dados e ao acesso não autorizado. As ineficiências na coordenação e comunicação entre os participantes da cadeia de abastecimento podem levar a atrasos, aumento de custos e diminuição da satisfação do cliente.

A solução: Rastreamento descentralizado da cadeia de suprimentos com contratos inteligentes

A tecnologia Blockchain, com as suas características inerentes de descentralização, transparência e imutabilidade, apresenta uma solução promissora para os desafios enfrentados pelas cadeias de abastecimento tradicionais. Contratos inteligentes, códigos autoexecutáveis ​​implantados na blockchain, fornecem uma maneira automatizada e segura de fazer cumprir acordos.

Ao implementar um aplicativo descentralizado de rastreamento da cadeia de suprimentos usando contratos inteligentes, podemos estabelecer um livro-razão digital à prova de falsificação que registra todas as etapas da jornada de um item. Este livro-razão é acessível a todas as partes autorizadas, garantindo transparência e rastreabilidade, ao mesmo tempo que reduz a necessidade de intermediários.

Escopo do projeto

Nosso projeto visa desenvolver um sistema de rastreamento da cadeia de suprimentos que permita aos usuários solicitar, cancelar e rastrear itens usando um aplicativo descentralizado. Isso envolve a criação de um contrato inteligente que gerencia todo o processo e um frontend que interage com o contrato. O contrato inteligente incluirá funções para solicitar itens, cancelar pedidos, atualizar status de itens e recuperar informações de status.

Benefícios da Solução

A solução proposta oferece uma infinidade de benefícios:

  1. Transparência e Rastreabilidade: Com dados registrados em uma blockchain imutável, todas as partes interessadas podem acompanhar a jornada de um item em tempo real, garantindo transparência e aumentando a confiança.
  2. Eficiência: A automação por meio de contratos inteligentes agiliza processos, reduz atrasos e elimina burocracia, levando a uma maior eficiência em toda a cadeia de abastecimento.
  3. Custos Reduzidos: Ao eliminar intermediários e reduzir as intervenções manuais, os custos associados à papelada, reconciliação e resolução de disputas são minimizados.
  4. Segurança de dados: A natureza descentralizada da solução torna-a resiliente a violações de dados e acesso não autorizado, aumentando a segurança das informações confidenciais da cadeia de abastecimento.
  5. Confiança e responsabilidade: A natureza inviolável da blockchain garante que as informações registradas sejam confiáveis, promovendo a confiança entre os participantes da cadeia de suprimentos.
  6. Auditoria e Conformidade: A natureza transparente e auditável da blockchain simplifica a conformidade com regulamentos e requisitos de auditoria.

Construindo o Contrato Inteligente

Vamos começar escrevendo o contrato inteligente usando Solidity, a linguagem de programação para contratos inteligentes Ethereum:

//contracts/SupplyChain.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract SupplyChain is Ownable {
   enum Status {
       Ordered,
       Shipped,
       Delivered,
       Cancelled
   }

   struct Item {
       uint id;
       string name;
       Status status;
       address orderedBy;
       address approvedBy;
       address deliveredTo;
   }

   mapping(uint => Item) private items;
   uint private itemCount;

   function orderItem(string memory _name) public {
       Item memory newItem = Item({
           id: itemCount,
           name: _name,
           status: Status.Ordered,
           orderedBy: msg.sender,
           approvedBy: address(0),
           deliveredTo: address(0)
       });
       items[itemCount] = newItem;
       itemCount++;
   }

   function cancelItem(uint _id) public {
       require(
           items[_id].orderedBy == msg.sender,
           "Somente a pessoa que encomendou o item pode cancelá-lo"
       );
       require(
           items[_id].status == Status.Ordered,
           "O item só pode ser cancelado se estiver no estado Encomendado"
       );
       items[_id].status = Status.Cancelled;
   }

   function approveItem(uint _id) public onlyOwner {
       require(
           items[_id].status == Status.Ordered,
           "O Item deve estar no estado Encomendado para ser aprovado"
       );
       items[_id].status = Status.Shipped;
       items[_id].approvedBy = msg.sender;
   }

   function shipItem(uint _id) public onlyOwner {
       require(
           items[_id].status == Status.Shipped,
           "O item deve estar no estado Enviado para ser enviado"
       );
       items[_id].status = Status.Delivered;
       items[_id].deliveredTo = items[_id].orderedBy;
   }

   function getItemStatus(uint _id) public view returns (Status) {
       return items[_id].status;
   }

   function getItem(uint _id) public view returns (Item memory) {
       return items[_id];
   }

   function getItemCount() public view returns (uint) {
       return itemCount;
   }
}
Enter fullscreen mode Exit fullscreen mode

Implantação

Implante o código do Contrato Inteligente.

//scripts/deploy.ts
import { ethers } from "hardhat";

async function main() {
 const currentTimestampInSeconds = Math.round(Date.now() / 1000);
 const unlockTime = currentTimestampInSeconds + 60;

 const lockedAmount = ethers.utils.parseEther("0.001");

 const Lock = await ethers.getContractFactory("SupplyChain");
 const lock = await Lock.deploy();

 await lock.deployed();

 console.log(
   `Lock with ${ethers.utils.formatEther(lockedAmount)}ETH and unlock timestamp ${unlockTime} deployed to ${lock.address}`
 );
}

main().catch((error) => {
 console.error(error);
 process.exitCode = 1;
});
Enter fullscreen mode Exit fullscreen mode

Escrevendo testes com ethers.js

Para garantir a exatidão do nosso contrato inteligente, precisamos escrever testes. Usaremos ethers.js, uma biblioteca JavaScript para interagir com Ethereum:

//test/supplyChain.ts
import { ethers } from "hardhat";
import { Contract } from "ethers";
import { expect } from "chai";

describe("SupplyChain", function () {
 let supplyChain: Contract;
 let owner: any;
 let addr1: any;
 let addr2: any;

 beforeEach(async function () {
   const SupplyChain = await ethers.getContractFactory("SupplyChain");
   [owner, addr1, addr2] = await ethers.getSigners();
   supplyChain = await SupplyChain.deploy();
   await supplyChain.deployed();
 });

 it("Deve criar um item e recuperar seu status", async function () {
   await supplyChain.orderItem("Item 1");
   const status = await supplyChain.getItemStatus(0);
   expect(status).to.equal(0);
 });

 it("Deve cancelar um item se estiver no estado Encomendado", async function () {
   await supplyChain.orderItem("Item 1");
   await supplyChain.cancelItem(0);
   const status = await supplyChain.getItemStatus(0);
   expect(status).to.equal(3);
 });

 it("Não deve permitir que não proprietários aprovem um item", async function () {
   await supplyChain.orderItem("Item 1");
   await expect(supplyChain.connect(addr1).approveItem(0)).to.be.revertedWith("Ownable: caller is not the owner");
 });

 it("Deve aprovar um item se estiver no estado Encomendado", async function () {
   await supplyChain.orderItem("Item 1");
   await supplyChain.approveItem(0);
   const item = await supplyChain.getItem(0);
   expect(item.status).to.equal(1);
   expect(item.approvedBy).to.equal(owner.address);
 });

 it("Não deve permitir que não proprietários enviem um item", async function () {
   await supplyChain.orderItem("Item 1");
   await supplyChain.approveItem(0);
   await expect(supplyChain.connect(addr1).shipItem(0)).to.be.revertedWith("Ownable: caller is not the owner");
 });

 it("Deve enviar um item se estiver no estado Enviado", async function () {
   await supplyChain.orderItem("Item 1");
   await supplyChain.approveItem(0);
   await supplyChain.shipItem(0);
   const item = await supplyChain.getItem(0);
   expect(item.status).to.equal(2);
   expect(item.deliveredTo).to.equal(owner.address);
 });
});
Enter fullscreen mode Exit fullscreen mode

Snippet de código de front-end

import { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import detectEthereumProvider from '@metamask/detect-provider';

//ln -s ../../smart-contract/artifacts/contracts/SupplyChain.sol/SupplyChain.json node_modules/SupplyChain.json
import SupplyChainABI from "SupplyChain.json";

import InputField from './InputField';
import Button from './button';

// Altere o endereço do contrato para corresponder ao endereço do contrato implantado
const contractAddress = "0x0B8693d0eD71BBd841ECE42BC8A36141737A4F4b";

function SupplyChain() {
 const [itemName, setItemName] = useState('');
 const [itemId, setItemId] = useState(0);
 const [itemDetails, setItemDetails] = useState<any>(null);
 const [items, setItems] = useState<any>([]);
 const [loading, setLoading] = useState(false);

 useEffect(() => {
   const initializeMetaMask = async () => {
     const provider: any = await detectEthereumProvider();
     if (provider) {
       await provider.request({ method: 'eth_requestAccounts' });

       const ethersProvider = new ethers.providers.Web3Provider(provider);
       const signer = ethersProvider.getSigner();
       const supplyChainContract = new ethers.Contract(contractAddress, SupplyChainABI.abi, signer);
       // Atualiza a instância do contrato com o novo signatário
       setSupplyChainContract(supplyChainContract);
     } else {
       console.error('MetaMask não encontrada');
     }
   };

   initializeMetaMask();
 }, []);

 const [supplyChainContract, setSupplyChainContract] = useState<any>(null);

 useEffect(() => {
   if (supplyChainContract) {
     loadItems();
   }
   // eslint-disable-next-line react-hooks/exhaustive-deps
 }, [supplyChainContract]);

 const loadItems = async () => {
   try {
     setLoading(true);
     const count = await supplyChainContract.getItemCount();
     const itemsArray: any = [];
     for (let i = 0; i < count.toNumber(); i++) {
       const item = await supplyChainContract.getItem(i);
       itemsArray.push(item);
     }
     setItems(itemsArray);
     setLoading(false);
     setItemName("")
   } catch (error) {
     console.error('Erro ao carregar items:', error);
     setLoading(false);
   }
 };

 const orderItem = async () => {
   try {
     const tx = await supplyChainContract.orderItem(itemName);
     await tx.wait();
     console.log('Item encomendado com sucesso!');
     loadItems();
   } catch (error) {
     console.error('Erro ao encomendar item:', error);
   }
 };

 const cancelItem = async (id: any) => {
   try {
     const tx = await supplyChainContract.cancelItem(id);
     await tx.wait();
     console.log('Item cancelado com sucesso!');
     loadItems();
   } catch (error) {
     console.error('Erro ao cancelar item:', error);
   }
 };

 const approveItem = async (id: any) => {
   try {
     const tx = await supplyChainContract.approveItem(id);
     await tx.wait();

     console.log('Item approvado com sucesso!');
     loadItems();
   } catch (error) {
     console.error('Erro ao aprovar item:', error);
   }
 };

 const shipItem = async (id: any) => {
   try {
     const tx = await supplyChainContract.shipItem(id);
     await tx.wait();

     console.log('Item enviado com sucesso!');
     loadItems();
   } catch (error) {
     console.error('Erro ao enviar item:', error);
   }
 };

 const getItem = async () => {
   try {
     setLoading(true);

     const item = await supplyChainContract.getItem(itemId);
     console.log('Item:', item);
     setItemDetails(item);
     setLoading(false);
   } catch (error) {
     console.error('Erro ao obter item:', error);
     setLoading(false);
   }
 };

 function getStatusText(status: number): string {
   switch (status) {
     case 0:
       return "Encomendado";
     case 1:
       return "Aprovado";
     case 2:
       return "Enviado";
     case 3:
       return "Cancelado";
     default:
       return "";
   }
 }

 function displayPartialAddress(address: string) {
   if (address.length <= 7) {
     return address;
   } else {
     const firstThree = address.substring(0, 3);
     const lastFour = address.substring(address.length - 4);
     return `${firstThree}...${lastFour}`;
   }
 }

 const cols = [
   "ID", "Nome", "Status", "Encomendado por", "Aprovado por", "Entregue para"
 ];

 return (
   <>
     <div className="overflow-hidden rounded-lg border border-gray-200 shadow-md m-5" data-aos="fade-up" data-aos-offset="300" data-aos-easing="ease-in-sine">
       <div className="m-10 flex justify-between space-x-5">
         <div className="w-full md:w-1/2 flex justify-between items-center space-x-3">
           <InputField
             value={itemName}
             onchange={(e: string) => setItemName(e)}
             placeholder="Digite seu item aqui ..."
           />
           <Button
             title="Order"
             onClick={orderItem}
             disabled={itemName === ""}
           />
         </div>
         <div className="w-full md:w-1/2 flex justify-between items-center space-x-3">
           <InputField
             value={itemId}
             type={"number"}
             onchange={(e: any) => setItemId(e)}
             placeholder="Entre o ID do item..."
           />
           <Button
             title="Search"
             onClick={getItem}
             disabled={itemId < 0}
           />

           <button className='mx-5 text-blue-600' onClick={loadItems}>
             <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" className="w-6 h-6">
               <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
             </svg>
           </button>
         </div>
       </div>
       <div className='m-10'>
         <div className="mb-4">
           {loading ? (
             <div className="text-center text-green-600">Loading...</div>
           ) : itemDetails && (
             <div className="border border-gray-300 p-4 rounded">
               <div>Item ID: {`${itemDetails.id}`}</div>
               <div className='text-sm'>Name: {itemDetails.name}</div>
               <div className='text-sm'>Status: {getStatusText(itemDetails.status)}</div>
               <div className='text-sm'>Ordered By: {itemDetails.orderedBy}</div>
               <div className='text-sm'>Approved By: {itemDetails.approvedBy}</div>
               <div className='text-sm'>Delivered To: {itemDetails.deliveredTo}</div>
             </div>
           )}
         </div>
       </div>
       <table className="w-full border-collapse bg-white text-left text-sm text-gray-500">
         <thead className="bg-gray-50">
           <tr>
             {cols.map((col) => (
               <th
                 scope="col"
                 className="px-6 py-4 font-medium text-gray-900"
                 key={col}
               >
                 {col}
               </th>
             ))}
             <th
               scope="col"
               className="px-6 py-4 text-center font-medium text-gray-900"
             >
               Actions
             </th>
           </tr>
         </thead>
         <tbody className="divide-y divide-gray-100 border-t border-gray-100">
           {items.length < 1 ? (
             <tr>
               <td
                 colSpan={cols.length + 1}
                 className="text-center text-xl py-10"
               >
                 No items to display
               </td>
             </tr>
           ) :
             (
               items
                 .sort((a: any, b: any) => b.id - a.id)
                 .map((item: any, index: number) => (
                   <tr className="hover:bg-gray-50" key={index}>
                     <th className="flex gap-3 px-6 py-2 font-normal text-gree-900">
                       {`${item.id}`}
                     </th>
                     <td
                       className="px-6 py-2 cursor-pointer"
                       onClick={() => setItemDetails(item)}
                     >
                       {item.name}
                     </td>
                     <td className="px-6 py-2">
                       <span
                         className={`inline-flex items-center gap-1 rounded-full bg-green-50 px-2 py-1 text-xs font-semibold ${item.status === 3
                             ? 'text-red-600'
                             : 'text-green-600'
                           }`}
                       >
                         <span
                           className={`h-1.5 w-1.5 rounded-full  ${item.status === 3
                               ? 'bg-red-600'
                               : 'bg-green-600'
                             }`}
                         />
                         {getStatusText(item.status)}
                       </span>
                     </td>
                     <td className="px-6 py-2">
                       {displayPartialAddress(item.orderedBy)}
                     </td>
                     <td className="px-6 py-2">
                       {displayPartialAddress(item.approvedBy)}
                     </td>
                     <td className="px-6 py-2">
                       {displayPartialAddress(item.deliveredTo)}
                     </td>
                     <td className="px-6 py-2 text-center">
                       <div className="flex justify-end space-x-3 gap-4">
                         {item.status === 0 && (
                           <>

                             <button className='text-red-600' onClick={() => cancelItem(item.id)}>Cancel</button>
                             <button className='text-green-600' onClick={() => approveItem(item.id)}>Approve</button>
                           </>
                         )}
                         {item.status === 1 && (
                           <button className='text-blue-600' onClick={() => shipItem(item.id)}>Ship Item</button>
                         )}
                       </div>
                     </td>
                   </tr>
                 ))
             )}
         </tbody>
       </table>
     </div>
   </>
 );
}

export default SupplyChain;
Enter fullscreen mode Exit fullscreen mode

A implementação completa

A implementação completa do aplicativo descentralizado de rastreamento da cadeia de suprimentos, juntamente com o contrato inteligente e o frontend, pode ser encontrada no GitHub. Este repositório contém o código do contrato inteligente Solidity, código de front-end JavaScript usando Ethers.js e instruções detalhadas de configuração.

Ao explorar este repositório, você pode obter uma compreensão mais profunda de como construir e implantar um aplicativo descentralizado de rastreamento da cadeia de suprimentos que aproveita o poder dos contratos inteligentes e da tecnologia blockchain.

Conclusão

O potencial da blockchain e dos contratos inteligentes para revolucionar o gerenciamento da cadeia de suprimentos é inegável. Ao desenvolver uma aplicação descentralizada de rastreamento da cadeia de abastecimento, enfrentamos os desafios de transparência e rastreabilidade, oferecendo uma solução que aumenta a eficiência, reduz custos e cria confiança entre todas as partes interessadas. À medida que abraçamos este futuro impulsionado pela tecnologia, a forma como gerimos e otimizamos as cadeias de abastecimento deverá passar por uma mudança transformadora que beneficiará tanto as empresas como os consumidores.

Este artigo foi escrito por Bizenforce e traduzido por Diogo Jorge. O artigo original pode ser encontrado aqui.

Top comments (0)