Sistema completo para registro, gestão e tokenização de cavalos Campolina com integração blockchain na Scroll Sepolia Testnet.
O HorShare é um sistema de registro equino que permite gerenciar:
- Usuários (pessoas físicas ou jurídicas)
- Haras/Fazendas (propriedades rurais)
- Cavalos e sua genealogia completa
- Registros oficiais (provisórios e definitivos)
- Mensurações técnicas realizadas por profissionais
- Tokenização blockchain - NFTs (ERC-721) e tokens de cotas (ERC-20)
- NestJS - Framework Node.js progressivo e modular
- TypeScript - Linguagem com tipagem estática
- Prisma ORM - ORM type-safe para acesso ao banco de dados
- SQLite - Banco de dados relacional (MVP - migra para PostgreSQL em produção)
- JWT (JSON Web Tokens) - Autenticação stateless
- Passport - Middleware de autenticação
- bcrypt - Hash seguro de senhas
- class-validator - Validação de DTOs
- class-transformer - Transformação de objetos
- ESLint - Linter para qualidade de código
- Prettier - Formatação consistente
- ethers.js v6 - Biblioteca para interação com Ethereum/EVM
- Scroll Sepolia Testnet - Layer 2 otimista para Ethereum
- ERC-721 - Padrão NFT para representar cavalos únicos
- ERC-20 - Padrão de token fungível para cotas de cavalos
┌─────────────────────────────────────────────────────────────┐
│ FRONTEND (React + Web3) │
│ - Usuário conecta MetaMask │
│ - Preenche formulário de registro │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ BACKEND API (NestJS + Prisma) │
│ - Valida dados do cavalo │
│ - Salva no banco de dados SQLite │
│ - Gera hashes (microchip + registro) │
│ - Backend assina transação (REGISTRAR_ROLE) │
│ - NFT mintado para carteira do usuário │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ BLOCKCHAIN (Scroll Sepolia Testnet) │
│ - HorseRegistry (0x4f7d...6A3B) - NFTs ERC-721 │
│ - HorseSharesFactory (0x1223...c815) - Factory ERC-20 │
│ - Hashes imutáveis de microchip e registro │
└─────────────────────────────────────────────────────────────┘
- Usuário registra cavalo → Dados salvos no banco de dados
- Backend valida → Verifica registro definitivo + mensuração
- Backend gera hashes:
microchipHash = keccak256(microchip)registroHash = keccak256(JSON canônico do registro + mensuração)
- Backend assina transação → Carteira com
REGISTRAR_ROLEpaga gas fees - NFT mintado on-chain → Enviado diretamente para carteira MetaMask do usuário
- Backend atualiza DB → Salva
tokenId,txHashecontractAddress
Representa usuários do sistema, incluindo proprietários, criadores e responsáveis por registros.
Schema:
model User {
id String @id @default(uuid())
nome String
sobrenome String
cpf String @unique
dataNascimento DateTime
email String @unique
senhaHash String
criadoEm DateTime @default(now())
}Representa propriedades rurais (haras ou fazendas) vinculadas a um proprietário.
Schema:
model Haras {
id String @id @default(uuid())
nome String
proprietarioId String
cidade String
estado String
cep String
proprietario User @relation(fields: [proprietarioId])
}Entidade central do sistema, representando os equinos registrados com campos blockchain.
Schema:
model Cavalo {
id String @id @default(uuid())
nome String
microchip String @unique
sexo String // "M" ou "F"
dataNascimento DateTime
// Blockchain Integration
nftTokenId String? @unique
nftContractAddress String?
nftBlockchainDbId Int? @unique // ID numérico para blockchain
sharesContractAddress String?
sharesTokenSymbol String?
sharesTotalSupply Int?
// Relações
harasNascimentoId String
harasAtualId String
criadorId String
proprietarioAtualId String
paiId String?
maeId String?
harasNascimento Haras @relation("HarasNascimento")
harasAtual Haras @relation("HarasAtual")
criador User @relation("Criador")
proprietarioAtual User @relation("Proprietario")
registros Registro[]
pai Cavalo? @relation("Pai")
mae Cavalo? @relation("Mae")
}Novos Campos Blockchain:
nftTokenId: ID do token ERC-721 na blockchainnftContractAddress: Endereço do contrato HorseRegistrynftBlockchainDbId: ID numérico sequencial usado no contratosharesContractAddress: Endereço do token ERC-20 de cotas (se criado)sharesTokenSymbol: Símbolo do token de cotas (ex: "SPIRIT")sharesTotalSupply: Total de cotas emitidas
Representa atos oficiais relacionados ao cavalo, como registro provisório ou definitivo.
Schema:
model Registro {
id String @id @default(uuid())
data DateTime @default(now())
tipo String // "PROVISORIO" ou "DEFINITIVO"
numeroRegistro String @unique
userId String
cavaloId String
user User @relation(fields: [userId])
cavalo Cavalo @relation(fields: [cavaloId])
mensuracao Mensuracao?
}Armazena dados técnicos e morfológicos do cavalo, vinculados a um registro específico.
Schema:
model Mensuracao {
id String @id @default(uuid())
registroId String @unique
veterinarioResponsavel String
pelagem String?
particularidades String?
marcasSinais String?
alturaCernelha Float?
alturaDorso Float?
alturaGarupa Float?
alturaCostados Float?
perimetroCanela Float?
perimetroTorax Float?
comprimentoCabeca Float?
comprimentoPescoco Float?
comprimentoDorsoLombo Float?
comprimentoGarupa Float?
comprimentoEspadua Float?
comprimentoCorpo Float?
larguraCabeca Float?
larguraAncas Float?
larguraPeito Float?
registro Registro @relation(fields: [registroId])
}Endereço: 0x4f7dBB2F9353Bfdd95BD153912405c2559Bb6A3B
Contrato principal para NFTs de cavalos.
Funções Principais:
mintHorseFromDb()- Mintar NFT (apenas REGISTRAR_ROLE)updateRegistroHash()- Atualizar hash do registroupdateTokenURI()- Atualizar metadata URIsetActive()- Ativar/desativar cavaloownerOf()- Consultar dono do NFTtokenURI()- Consultar metadata URI
Roles & Permissões:
DEFAULT_ADMIN_ROLE- Pode conceder/revogar outras rolesREGISTRAR_ROLE- Pode mintar NFTs e atualizar registrosPAUSER_ROLE- Pode pausar/despausar o contrato
Endereço: 0x1223aFa91995e88a838c0080c8fd9414C808c815
Factory para criar tokens ERC-20 de cotas de cavalos.
Função Principal:
createShares()- Criar token de cotas (apenas dono do NFT)
Regra de Negócio:
- Apenas o proprietário do NFT pode criar o token de cotas
- Cada cavalo pode ter apenas um token de cotas
- O supply inicial vai para o dono do NFT
- Node.js 20+ instalado
- npm ou yarn
cd src
npm installCrie um arquivo .env na pasta src/:
# Database
DATABASE_URL="file:./prisma/dev.db"
# JWT
JWT_SECRET="seu-secret-key-aqui-mude-em-producao"
# Server
PORT=3001
FRONTEND_URL="http://localhost:3000"
# Blockchain - Scroll Sepolia Testnet
BLOCKCHAIN_RPC_URL="https://sepolia-rpc.scroll.io"
BLOCKCHAIN_CHAIN_ID="534351"
# Contratos Deployados
ERC721_CONTRACT_ADDRESS="0x4f7dBB2F9353Bfdd95BD153912405c2559Bb6A3B"
ERC20_FACTORY_ADDRESS="0x1223aFa91995e88a838c0080c8fd9414C808c815"
# Carteira do Backend (precisa ter REGISTRAR_ROLE e ETH para gas)
# IMPORTANTE: Nunca commitar esta chave! Use secrets em produção
BLOCKCHAIN_PRIVATE_KEY="sua-private-key-sem-0x"
# Metadata
METADATA_BASE_URI="http://localhost:3001/blockchain/metadata"# Gerar Prisma Client
npx prisma generate
# Rodar migrations
npx prisma migrate dev
# (Opcional) Visualizar banco de dados
npx prisma studioModo Desenvolvimento:
npm run start:devModo Produção:
npm run build
npm run start:prodServidor disponível em: http://localhost:3001/api
POST /api/auth/login
Content-Type: application/json
{
"email": "usuario@email.com",
"senha": "senha123"
}GET /api/auth/profile
Authorization: Bearer {access_token}POST /api/users # Criar usuário
GET /api/users # Listar usuários
GET /api/users/:id # Buscar usuário
PATCH /api/users/:id # Atualizar usuário
DELETE /api/users/:id # Deletar usuárioPOST /api/haras # Criar haras
GET /api/haras # Listar haras
GET /api/haras/:id # Buscar haras
PATCH /api/haras/:id # Atualizar haras
DELETE /api/haras/:id # Deletar harasPOST /api/horses # Criar cavalo
GET /api/horses # Listar cavalos
GET /api/horses/:id # Buscar cavalo
GET /api/horses/:id/genealogy # Árvore genealógica
GET /api/horses/:id/descendants # Descendentes
PATCH /api/horses/:id # Atualizar cavalo
DELETE /api/horses/:id # Deletar cavaloPOST /api/registries # Criar registro
GET /api/registries # Listar registros
GET /api/registries/:id # Buscar registro
PATCH /api/registries/:id # Atualizar registro
DELETE /api/registries/:id # Deletar registroPOST /api/measurements # Criar mensuração
GET /api/measurements # Listar mensurações
GET /api/measurements/:id # Buscar mensuração
PATCH /api/measurements/:id # Atualizar mensuração
DELETE /api/measurements/:id # Deletar mensuraçãoGET /api/blockchain/infoResposta:
{
"network": "Scroll Sepolia Testnet",
"blockNumber": 15520882,
"contracts": {
"horseRegistry": {
"address": "0x4f7dBB2F9353Bfdd95BD153912405c2559Bb6A3B",
"type": "ERC-721",
"description": "Registro de cavalos (NFTs)"
},
"sharesFactory": {
"address": "0x1223aFa91995e88a838c0080c8fd9414C808c815",
"type": "Factory",
"description": "Factory de tokens de cotas (ERC-20)"
}
},
"rpcUrl": "https://sepolia-rpc.scroll.io"
}POST /api/blockchain/nft/mint
Content-Type: application/json
{
"cavaloId": "uuid-do-cavalo",
"ownerAddress": "0x..." // Endereço MetaMask do usuário
}Resposta:
{
"success": true,
"cavalo": {
"id": "5b2ff098-713a-49cf-a88f-42590029669a",
"nome": "Relâmpago Dourado",
"microchip": "456789012345678"
},
"nft": {
"tokenId": "3",
"transactionHash": "0xf8fca9ac192b66b966913a747f8e91bab03750edeea324cff8cd8233695b2a2a",
"ownerAddress": "0x9B5d0F6CDdCd7f059f681406D8B72eDbC1C0619D",
"metadataUri": "http://localhost:3001/blockchain/metadata/horse/...",
"contractAddress": "0x4f7dBB2F9353Bfdd95BD153912405c2559Bb6A3B"
}
}Verificar na Blockchain:
https://sepolia.scrollscan.com/tx/0xf8fca9ac192b66b966913a747f8e91bab03750edeea324cff8cd8233695b2a2a
GET /api/blockchain/nft/:tokenId/ownerResposta:
{
"tokenId": "3",
"owner": "0x9B5d0F6CDdCd7f059f681406D8B72eDbC1C0619D",
"metadataUri": "http://localhost:3001/blockchain/metadata/horse/...",
"cavalo": {
"id": "...",
"nome": "Relâmpago Dourado",
"microchip": "456789012345678"
},
"proprietarioAtual": {
"id": "...",
"nome": "João",
"sobrenome": "Silva",
"email": "joao@email.com"
}
}POST /api/blockchain/shares/create
Content-Type: application/json
{
"cavaloId": "uuid-do-cavalo",
"name": "Cotas Relâmpago Dourado",
"symbol": "RELAMP",
"totalSupply": 100,
"initialOwnerAddress": "0x..." // Endereço do dono do NFT
}Resposta:
{
"success": true,
"cavalo": {
"id": "...",
"nome": "Relâmpago Dourado"
},
"token": {
"contractAddress": "0xabc...",
"name": "Cotas Relâmpago Dourado",
"symbol": "RELAMP",
"totalSupply": 100,
"transactionHash": "0xdef...",
"initialOwner": "0x..."
}
}# Saldo de NFTs (ERC-721)
GET /api/blockchain/balance?address=0x...
# Saldo de cotas de um cavalo específico (ERC-20)
GET /api/blockchain/balance?address=0x...&cavaloId=uuidGET /api/blockchain/metadata/horse/:cavaloIdResposta:
{
"name": "Relâmpago Dourado",
"description": "Cavalo registrado na plataforma HorShare - Microchip: 456789012345678",
"image": "http://localhost:3001/blockchain/metadata/horse/.../image",
"external_url": "https://horshare.com/horse/...",
"attributes": [
{ "trait_type": "Microchip", "value": "456789012345678" },
{ "trait_type": "Sexo", "value": "Macho" },
{ "trait_type": "Data de Nascimento", "value": "2019-08-20" },
{ "trait_type": "Pelagem", "value": "Tordilha" },
{ "trait_type": "Altura na Cernelha", "value": 1.55, "display_type": "number" },
{ "trait_type": "Registro", "value": "DEF-2024-003" },
{ "trait_type": "Haras", "value": "Haras Muiraquitã" }
]
}# 1.1 Criar cavalo
curl -X POST http://localhost:3001/api/horses \
-H "Content-Type: application/json" \
-d '{
"nome": "Teste Blockchain",
"microchip": "111222333444555",
"sexo": "M",
"dataNascimento": "2020-01-01",
"harasNascimentoId": "uuid-haras",
"harasAtualId": "uuid-haras",
"criadorId": "uuid-user",
"proprietarioAtualId": "uuid-user"
}'
# 1.2 Criar registro definitivo
curl -X POST http://localhost:3001/api/registries \
-H "Content-Type: application/json" \
-d '{
"cavaloId": "uuid-do-cavalo",
"tipo": "DEFINITIVO",
"numeroRegistro": "DEF-2024-999",
"userId": "uuid-user"
}'
# 1.3 Criar mensuração
curl -X POST http://localhost:3001/api/measurements \
-H "Content-Type: application/json" \
-d '{
"registroId": "uuid-registro",
"pelagem": "Castanha",
"alturaCernelha": 1.50,
"perimetroTorax": 1.75,
"veterinarioResponsavel": "Dr. Teste"
}'curl -X POST http://localhost:3001/api/blockchain/nft/mint \
-H "Content-Type: application/json" \
-d '{
"cavaloId": "uuid-do-cavalo",
"ownerAddress": "0xSeuEnderecoMetaMask"
}'# Consultar dono do NFT
curl http://localhost:3001/api/blockchain/nft/3/owner
# Ver saldo de NFTs
curl "http://localhost:3001/api/blockchain/balance?address=0xSeuEndereco"
# Ver metadata
curl http://localhost:3001/api/blockchain/metadata/horse/uuid-do-cavalo# Criar token de cotas
curl -X POST http://localhost:3001/api/blockchain/shares/create \
-H "Content-Type: application/json" \
-d '{
"cavaloId": "uuid-do-cavalo",
"name": "Cotas Teste",
"symbol": "TEST",
"totalSupply": 100,
"initialOwnerAddress": "0xSeuEndereco"
}'
# Ver saldo de cotas
curl "http://localhost:3001/api/blockchain/balance?address=0xSeuEndereco&cavaloId=uuid-do-cavalo"Backend/
├── src/
│ ├── src/
│ │ ├── database/
│ │ │ ├── prisma.module.ts
│ │ │ └── prisma.service.ts
│ │ ├── modules/
│ │ │ ├── auth/ # Autenticação JWT
│ │ │ ├── blockchain/ # Integração Web3
│ │ │ │ ├── blockchain.controller.ts
│ │ │ │ ├── blockchain.service.ts
│ │ │ │ ├── blockchain-contract.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ └── blockchain.dto.ts
│ │ │ │ └── abis/
│ │ │ │ ├── HorseRegistry.json
│ │ │ │ ├── HorseSharesFactory.json
│ │ │ │ └── HorseShareToken.json
│ │ │ ├── haras/ # Propriedades rurais
│ │ │ ├── horse/ # Cavalos + Genealogia
│ │ │ ├── measurements/ # Mensurações técnicas
│ │ │ ├── registry/ # Registros oficiais
│ │ │ └── user/ # Usuários
│ │ ├── app.module.ts
│ │ └── main.ts
│ ├── prisma/
│ │ ├── schema.prisma # Schema com campos blockchain
│ │ ├── dev.db
│ │ └── migrations/
│ │ ├── 20251215203753_init/
│ │ └── 20251215214856_add_nft_blockchain_db_id/
│ ├── package.json
│ ├── tsconfig.json
│ └── .env
└── README.md
- Scroll Sepolia RPC: https://sepolia-rpc.scroll.io
- Scroll Explorer: https://sepolia.scrollscan.com
- Contrato HorseRegistry: https://sepolia.scrollscan.com/address/0x4f7dBB2F9353Bfdd95BD153912405c2559Bb6A3B
- Contrato Factory: https://sepolia.scrollscan.com/address/0x1223aFa91995e88a838c0080c8fd9414C808c815
- Scroll Faucet: https://docs.scroll.io/en/user-guide/faucet/
- NestJS Docs: https://docs.nestjs.com
- Prisma Docs: https://www.prisma.io/docs
- Ethers.js Docs: https://docs.ethers.org/v6/
- ✅ UserModule - Gestão completa de usuários
- ✅ AuthModule - Autenticação JWT com Passport
- ✅ HarasModule - CRUD de propriedades rurais
- ✅ HorseModule - CRUD de cavalos + Genealogia
- ✅ RegistryModule - Registros provisórios e definitivos
- ✅ MeasurementsModule - Mensurações técnicas
- ✅ BlockchainModule - NFTs (ERC-721) + Tokens de Cotas (ERC-20)
- ✅ Mint de NFT - Backend assina, usuário recebe
- ✅ Consulta de proprietário - Verificação on-chain
- ✅ Criação de tokens de cotas - ERC-20 por cavalo
- ✅ Metadata OpenSea compatible - JSON padrão NFT
- ✅ Hashes de integridade - Microchip + Registro imutáveis
- ✅ Verificação de saldos - NFTs e tokens de cotas
- ✅ Backend usa carteira com
REGISTRAR_ROLEpara mintar - ✅ NFT vai diretamente para carteira do usuário
- ✅ Hashes de microchip e registro são imutáveis on-chain
- ✅ Apenas dono do NFT pode criar token de cotas
- ✅ Contratos têm roles de admin, registrador e pauser
- ✅ Senhas hasheadas com bcrypt
- ✅ Autenticação JWT
- ✅ Validação de DTOs com class-validator
- ✅ Private keys em variáveis de ambiente (nunca no código)
# Testes unitários
npm run test
# Testes e2e
npm run test:e2e
# Cobertura de testes
npm run test:cov# Desenvolvimento
npm run start:dev
# Build
npm run build
# Produção
npm run start:prod
# Prisma
npx prisma generate # Gerar client
npx prisma migrate dev # Criar migration
npx prisma studio # UI visual do bancoEste backend está configurado para Scroll Sepolia Testnet. Para produção:
- Banco de Dados: Migrar de SQLite para PostgreSQL
- Variáveis de Ambiente: Usar secrets manager (AWS Secrets, HashiCorp Vault)
- Blockchain: Usar Scroll Mainnet com carteira segura (multisig recomendado)
- Private Keys: NUNCA commitar! Use HSM ou KMS em produção
- CORS: Configurar domínios permitidos
- Rate Limiting: Implementar para evitar abuso
- Logs: Centralizar com CloudWatch, Datadog ou similar
- Monitoring: Configurar alertas para transações falhadas
- Fork o projeto
- Crie uma branch (
git checkout -b feature/nova-feature) - Commit suas mudanças (
git commit -m 'Add nova feature') - Push para a branch (
git push origin feature/nova-feature) - Abra um Pull Request
Este projeto está sob a licença MIT.