Sistema de rastreamento de reciclagem de garrafas na blockchain Cardano usando smart contracts Plutus V2. Cada garrafa passa por 5 estágios (inserted -> compacted -> collected -> atstation -> shredded), e o reciclador recebe tokens Greentoken como recompensa a cada transição.
| Camada | Tecnologia | Descrição |
|---|---|---|
| On-chain | Haskell / Plutus V2 | Smart contract que valida transições de estágio |
| Off-chain | Bash + cardano-cli | Scripts para operar diretamente na blockchain |
| Backend | Node.js + TypeScript + PostgreSQL | API REST que integra blockchain e banco de dados |
| Frontend | React + TypeScript + Vite + TailwindCSS | Dashboard web para gerenciar o sistema |
Dashboard Frontend
|
|--- API REST (Backend Node.js)
| |
| |-- Submete transação na blockchain Cardano (cardano-cli)
| |-- Registra garrafa + tx pendente no PostgreSQL
|
| ~~~ confirmation worker (polling a cada 15s) ~~~
|
| |-- Detecta UTxO confirmado on-chain
| | |-- Atualiza tx -> confirmed
| | |-- Atualiza garrafa -> utxo_hash
| | |-- Registra recompensa Greentoken
|
|--- Dados disponíveis via API e renderizados no dashboard
| Operação | Transação on-chain | Detalhes |
|---|---|---|
| Criar garrafa | Mint de NFT + 10 Greentoken | Cria um UTxO no endereço do script Plutus com datum {user, bottleId, stage=inserted}. Minta 10 Greentoken e envia à carteira do reciclador. |
| Compactar | Advance stage (por garrafa) | Consome o UTxO inserted do script, cria novo UTxO com datum stage=compacted. Minta 5 Greentoken para o reciclador. Requer redeemer de transição. |
| Coletar | Advance stage (por garrafa) | Consome UTxO compacted, cria UTxO collected. Minta 5 Greentoken. |
| Entregar na estação | Advance stage (por garrafa) | Consome UTxO collected, cria UTxO atstation. Minta 10 Greentoken. |
| Triturar | Advance stage (por garrafa) | Consome UTxO atstation, cria UTxO shredded. Minta 20 Greentoken. |
Cada transação é submetida via cardano-cli, assinada pelo operador + policy key, e confirmada pelo confirmation worker que verifica a presença do UTxO no endereço do script a cada 15 segundos.
| Estágio | Greentoken | Descrição |
|---|---|---|
| inserted | 10 | Garrafa inserida no container |
| compacted | 5 | Garrafas do container compactadas |
| collected | 5 | Container coletado pelo caminhão |
| atstation | 10 | Garrafas entregues na estação |
| shredded | 20 | Garrafas trituradas na estação |
| Total | 50 | Por garrafa, do início ao fim |
greentoken-cardano/
|-- onchain/src/Greentoken/
| |-- BottleValidator.hs # Smart contract Plutus V2
|-- offchain/test/Greentoken/
| |-- BottleValidatorSpec.hs # Testes do validador
|-- app/
| |-- Main.hs # CLI para exportar o contrato
| |-- WriteBottleValidator.hs # Serialização do script
|-- assets/
| |-- bottle-validator.plutus # Contrato compilado
| |-- policy/ # Minting policy (policyID, script, chaves)
| |-- redeemers/ # Redeemers para cada transição de estágio
| |-- wallet/ # Endereço do operador e do script Plutus
| |-- users/ # Endereços e chaves dos usuários
|-- frontend/
| |-- src/
| | |-- components/ui/ # Componentes shadcn (Radix + Tailwind)
| | |-- hooks/ # Custom hooks (useSortable)
| | |-- lib/ # Utilitários (truncateMiddle, labels)
| | |-- pages/ # Páginas (Bottles, Users, Containers, Routes, Stations)
| | |-- services/api.ts # Cliente HTTP tipado para a API REST
| | |-- App.tsx # Layout principal com abas
| | |-- index.css # Tema verde Tailwind + variáveis CSS
| |-- vite.config.ts # Vite + proxy /api -> backend:3000
| |-- tailwind.config.js # Tailwind v3 + tokens shadcn
| |-- package.json
|-- backend/
| |-- db/schema.sql # Schema PostgreSQL (DDL + seed)
| |-- src/
| | |-- services/ # Lógica de negócio (bottle, container, cardano)
| | |-- routes/ # Endpoints Express
| | |-- db/queries/ # Queries PostgreSQL tipadas
| | |-- workers/ # Confirmation worker (polling blockchain)
| |-- .env.example # Template de variáveis de ambiente
| |-- package.json
| |-- tsconfig.json
|-- scripts/
| |-- _db-helper.sh # Helper: integração scripts <-> PostgreSQL
| |-- setup-wallet.sh # Setup: chaves do operador + endereço do script
| |-- setup-policy.sh # Setup: minting policy + policyID
| |-- split-utxos.sh # Fragmenta UTXO do operador para operações batch
| |-- query-balance.sh # Consulta saldo de qualquer endereço
| |-- get-pubkey-hash.sh # Helper: gerar pubkey hash a partir do addr da wallet
|-- SETUP-LOCAL.md # Guia completo de configuração local
|-- plutus-greentoken.cabal # Configuração Haskell
|-- cabal.project # Dependências Haskell
| Script | Função | Uso |
|---|---|---|
setup-wallet.sh |
Gera chaves do operador e endereço do script | scripts/setup-wallet.sh |
setup-policy.sh |
Gera minting policy e policyID | scripts/setup-policy.sh |
| Script | Função | Uso |
|---|---|---|
split-utxos.sh |
Fragmenta o UTXO do operador em N UTXOs menores | scripts/split-utxos.sh [N] (padrão: 10) |
query-balance.sh |
Saldo de qualquer endereço | scripts/query-balance.sh [ADDR|USER_ID] |
O backend roda na porta 3000 e expõe os seguintes endpoints:
| Método | Rota | Descrição |
|---|---|---|
GET |
/health |
Health check |
| Usuários | ||
GET |
/users |
Lista usuários (?role=recycler|owner) |
GET |
/users/:id |
Detalhe de um usuário |
POST |
/users |
Cria usuário |
GET |
/users/:id/rewards |
Recompensas + total Greentoken |
| Garrafas | ||
GET |
/bottles |
Lista garrafas (?user_id=, ?stage=, ?container_id=, ?route_id=, ?station_id=) |
GET |
/bottles/next-number |
Próximo número disponível para garrafa |
GET |
/bottles/:id |
Detalhe + histórico (txs + rewards) |
POST |
/bottles |
Cria garrafa (blockchain + banco) |
| Containers | ||
GET |
/containers |
Lista containers (?status=all|active|full|compacted|in_route, ?owner_id=) |
POST |
/containers |
Cria container |
POST |
/containers/:id/compact |
Compacta garrafas inserted (>= 90% cheio) |
POST |
/containers/:id/collected |
Marca como coletado (esvaziado) |
| Caminhões | ||
GET |
/trucks |
Lista caminhões |
POST |
/trucks |
Cadastra caminhão (license_plate) |
| Rotas | ||
GET |
/routes |
Lista rotas de coleta |
POST |
/routes |
Cria rota (truck_id + container_ids + station_id) |
GET |
/routes/:id |
Detalhe da rota com paradas |
POST |
/routes/stops/:stopId/collect |
Coleta parada (compacted -> collected) |
POST |
/routes/:id/deliver |
Entrega garrafas na estação (collected -> atstation) |
| Estações | ||
GET |
/stations |
Lista estações de tratamento |
POST |
/stations |
Cria estação |
GET |
/stations/:id/bottles |
Lista garrafas na estação |
POST |
/stations/:id/shred |
Tritura garrafas atstation (atstation -> shredded) |
Schema com 8 tabelas (backend/db/schema.sql):
users- recicladores e donos de pontos de coletacontainers- pontos físicos de coleta (volume, status: active/full/compacted/in_route/maintenance)trucks- frota de caminhões (status: available/on_route/maintenance)routes/route_stops- rotas de coleta com paradas em containersbottles- espelha os estágios do contrato Plutus (utxo_hash+utxo_index)blockchain_txs- log de auditoria de todas as transações submetidasrewards- registro de Greentoken creditados por estágiostations- estações de tratamento de resíduos
Consulte o SETUP-LOCAL.md para o guia completo. Resumo rápido:
# 1. Configurar PostgreSQL
sudo -u postgres psql -c "CREATE DATABASE greentoken_db;"
psql -U postgres -d greentoken_db -f backend/db/schema.sql
# 2. Gerar chaves do operador (wallet owner)
export CARDANO_NODE_SOCKET_PATH=~/cardano/preprod/node.socket
export CARDANO_NODE_MAGIC=1
scripts/setup-wallet.sh
scripts/setup-policy.sh
# 3. Financiar wallet do operador com tADA (via faucet testnet Preprod)
cat assets/wallet/payment.addr
# Cole o endereço no faucet: https://docs.cardano.org/cardano-testnets/tools/faucet
# 3.1. Fragmentar UTXOs do operador (necessário para operações batch)
scripts/split-utxos.sh 100
# Aguarde ~20s e verifique: scripts/query-balance.sh
# 4. Iniciar nó e backend
cardano-start
cd backend && cp .env.example .env && npm install && npm run dev
# 5. Iniciar o frontend
cd frontend && npm install && npm run dev
# Acesse http://localhost:5173
# 6. Criar usuários no frontend:
# - Owner: usar endereço de assets/wallet/payment.addr
# scripts/get-pubkey-hash.sh $(cat assets/wallet/payment.addr)
# - Recycler: criar wallet via Lace (extensão Chrome, rede Preprod)
# scripts/get-pubkey-hash.sh <ENDERECO_LACE>
# Recyclers NÃO precisam de tADA (transações financiadas pelo owner)Dashboard web construído com React + TypeScript + Vite + TailwindCSS v3 + shadcn/ui (componentes Radix UI).
- 5 abas: Usuários, Garrafas, Containers, Rotas/Caminhões, Estações de Tratamento
- CRUD completo: criar e visualizar registros de cada entidade via interface gráfica
- Bloqueios de fluxo: botões desabilitados conforme regras de negócio (ex: não compactar container < 90%, não entregar na estação sem coletar todas as paradas)
- Cooldown de blockchain: após criar uma garrafa, o botão fica desabilitado até a transação ser confirmada on-chain (polling automático a cada 5s)
- Rotas de coleta: selecionar caminhão + containers compactados + estação de destino, coletar paradas e entregar na estação
- Recompensas: dialog para visualizar recompensas Greentoken de cada usuário
- UX: botões de copiar, colunas ordenáveis, truncamento inteligente de endereços, tooltips explicativos, mensagens de erro formatadas
cd frontend
npm install
npm run dev # http://localhost:5173O Vite faz proxy de /api/* para http://localhost:3000 (backend). O backend precisa estar rodando.
Antes de usar o sistema, é necessário configurar as wallets Cardano para cada tipo de usuário:
A wallet do owner é criada via script e é responsável por financiar todas as transações on-chain (mint de NFTs, transições de estágio, recompensas). Sem ela, nenhuma operação na blockchain funciona.
- Gere a wallet com
scripts/setup-wallet.sh(cria chaves emassets/wallet/) - Financie a wallet com tADA via faucet da testnet Preprod
- Com o endereço (
payment.addr) e o pubkey hash (scripts/get-pubkey-hash.sh), crie o usuário owner no frontend
As wallets dos recicladores servem para identificar o usuário e receber recompensas Greentoken. As transações são financiadas pela wallet do owner, portanto recicladores não precisam de tADA.
- Recomendado: Crie a wallet pela extensão Lace Wallet no Chrome (rede Preprod)
- Alternativa: Crie via
cardano-cli(ver SETUP-LOCAL.md) - Com o endereço da wallet e o pubkey hash (
scripts/get-pubkey-hash.sh <ENDERECO>), crie o usuário recycler no frontend
Consulte o SETUP-LOCAL.md para instruções detalhadas passo a passo.
O sistema segue um fluxo sequencial com bloqueios para evitar que etapas sejam puladas:
1. Configurar Wallets (owner via script + recycler via Lace/script)
|
2. Criar Usuários no frontend (owner e recycler, com wallet address + pubkey hash)
|
3. Criar Container (associado a um proprietário)
|
4. Inserir Garrafa -> associada a um container e um usuário
| (garrafa: inserted | recompensa: 10 Greentoken)
| [botão bloqueado até confirmação on-chain da garrafa anterior]
|
5. Container >= 90% -> botão "Compactar" habilitado
| (garrafa: inserted -> compacted | recompensa: 5 Greentoken)
| [container muda para status "compactado" após compactação]
|
6. Criar Rota de Coleta
| (selecionar caminhão disponível + containers compactados + estação de destino)
|
7. Coletar Paradas da Rota (uma a uma)
| (garrafa: compacted -> collected | recompensa: 5 Greentoken)
| [garrafas saem do container e ficam associadas ao caminhão]
| [container volta a status "ativo" com volume zerado]
|
8. Entregar na Estação (só após TODAS as paradas coletadas)
| (garrafa: collected -> atstation | recompensa: 10 Greentoken)
| [garrafas saem do caminhão e ficam associadas à estação]
|
9. Triturar na Estação
(garrafa: atstation -> shredded | recompensa: 20 Greentoken)
Total de recompensa por garrafa: 50 Greentoken
- Frontend web para recicladores e owners
- Implementar lógica de rotas de caminhão (CRUD de caminhões, criação de rotas, coleta de paradas)
- Estações de tratamento e trituração
- Bloqueios de fluxo no frontend (evitar etapas fora de ordem)
- Cooldown de blockchain na criação de garrafas
- Correção de recompensas em operações batch multi-usuário
- Lidar com delays de operações na blockchain (validação de UTXOs confirmados + fragmentação de UTXOs)
- Adicionar autenticação na API
- Testes automatizados para o backend
- Migrar de
child_processparacardano-serialization-lib - Deploy em produção (mainnet)