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:
- 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.
- 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.
- 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.
- 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.
- 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.
- 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;
}
}
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;
});
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);
});
});
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;
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.
Latest comments (0)