- Visão Geral
- Conceitos Fundamentais
- Estrutura do Contrato
- Funções Administrativas
- Sistema de Pagamentos
- Sistema de Disputas
- Sistema de Aprovações
- Funções Automáticas
- Sistema de Acordos
- Funções de Consulta
- Fluxo de Operação
- Segurança e Proteções
- Exemplos Práticos
- Glossário
O contrato Escrow é uma solução descentralizada para facilitar transações seguras entre partes que não confiam mutuamente. Funciona como um intermediário confiável que guarda fundos até que todas as condições do acordo sejam satisfeitas.
Imagine que você quer comprar um carro de uma pessoa desconhecida pela internet:
- Problema: Você não confia em pagar primeiro, e ele não confia em entregar primeiro
- Solução: Um "intermediário confiável" (o escrow) guarda o dinheiro até tudo dar certo
- Como funciona: Você deposita o dinheiro → Ele entrega o carro → O intermediário libera o pagamento
Um mecanismo de segurança que retém fundos até que condições específicas sejam atendidas, garantindo que ambas as partes cumpram suas obrigações.
Depósitos feitos pelas partes para assegurar o cumprimento do contrato:
- Comprador: Garantia de pagamento
- Vendedor: Garantia de entrega
Mecanismo de resolução de conflitos quando uma das partes não cumpre suas obrigações.
O contrato Escrow é composto por três componentes principais:
- BaseEscrow: Classe abstrata com funcionalidades básicas
- Escrow: Implementação principal do sistema
- EscrowLib: Biblioteca com funções auxiliares
Função: startEscrow()
Descrição: Oficialmente inicia a custódia após todas as garantias serem fornecidas.
Analogia: É como apertar o botão "INICIAR" em uma máquina de lavar:
- Tudo precisa estar no lugar (água, sabão, roupas)
- Só depois você pode apertar o botão
- Uma vez iniciado, o ciclo começa a rodar
Quando usar: Depois que comprador e vendedor depositaram suas garantias.
Função: payInstallmentETH(uint256 installmentAmount)
Descrição: Permite ao comprador pagar uma parcela usando Ethereum (moeda digital).
Analogia: Como pagar um boleto no banco:
- Se pagar no prazo → só o valor normal
- Se atrasar → o sistema cobra juros automaticamente
- Pagou a mais? O troco volta automaticamente
Exemplo prático:
- Parcela de R$ 1.000 vence hoje
- Você atrasou 5 dias = R$ 50 de juros
- Total a pagar: R$ 1.050
- Enviou R$ 1.100? Recebe R$ 50 de troco
Função: payInstallmentERC20(address token, uint256 installmentAmount)
Descrição: Igual ao anterior, mas usando tokens (outras moedas digitais).
Analogia: Como pagar com cartão de débito em vez de dinheiro vivo.
Função: payAllRemaining()
Descrição: Permite pagar todas as parcelas restantes de uma vez.
Analogia: Como quitar um financiamento:
- Em vez de pagar 12x de R$ 500
- Você decide pagar R$ 6.000 de uma vez
- Acaba mais rápido e sem risco de juros futuros
Função: calculateInstallmentWithInterest(uint256 installmentNumber)
Descrição: Calcula quanto você deve pagar agora, incluindo juros se estiver atrasado.
Analogia: Como o taxímetro do Uber:
- Dentro do tempo estimado → preço normal
- Trânsito parado (atraso) → o taxímetro continua rodando
- No final, você paga o valor base + o tempo extra
Tipos de juros:
- Simples: Cada dia soma 1% sobre o valor original
- Compostos: Juros sobre juros (como cartão de crédito)
Função: openDispute(string memory reason)
Descrição: Quando algo dá errado, qualquer parte pode abrir uma disputa.
Analogia: Como abrir um processo no Procon:
- Comprador: "Ele não entregou o que prometeu!"
- Vendedor: "Ele não pagou direito!"
- Sistema: "Ok, vamos parar tudo até resolver isso"
Efeito: Congela todas as ações até alguém resolver.
Função: resolveDispute(uint256 buyerPercentage, uint256 sellerPercentage)
Descrição: Um mediador resolve a disputa e divide o dinheiro conforme sua decisão.
Analogia: Como um juiz no tribunal:
- Analisa as evidências
- Decide: "60% para o comprador, 40% para o vendedor"
- A decisão é final e automática
Flexibilidade total: Não é só "tudo ou nada" - pode dividir como achar justo.
Função: setReleaseApproval(bool approved)
Descrição: Cada participante (comprador, vendedor, mediador) dá sua aprovação.
Analogia: Como três chaves para abrir um cofre do banco:
- Cada pessoa tem uma chave
- Só abre quando as três chaves girarem juntas
- Qualquer um pode voltar atrás até a abertura final
Função: withdrawFunds()
Descrição: O vendedor retira o dinheiro quando tudo estiver aprovado.
Analogia: Como sacar dinheiro no caixa eletrônico:
- Precisa da senha (aprovações)
- Precisa que a conta tenha saldo
- O banco cobra uma pequena taxa de serviço
Condições:
- ✅ Todos aprovaram OU contrato já finalizou
- ✅ Não tem disputa ativa
- ✅ Há dinheiro para sacar
Função: returnGuarantee()
Descrição: Devolve a garantia (dinheiro/NFT/token) para o comprador.
Analogia: Como receber o depósito do aluguel de volta:
- Você pagou R$ 2.000 de caução
- Não fez bagunça na casa
- No final, recebe os R$ 2.000 de volta
Tipos de garantia suportados:
- 💰 Dinheiro (ETH)
- 🪙 Tokens (ERC-20)
- 🖼️ NFTs (ERC-721)
- 📦 Tokens colecionáveis (ERC-1155)
Função: _checkAutoComplete()
Descrição: Automaticamente finaliza o contrato quando detecta consenso total.
Analogia: Como um assistente que percebe quando todo mundo concordou:
- Pagamentos: ✅ Completos
- Aprovações: ✅ Todos deram OK
- Disputas: ✅ Nenhuma ativa
- Resultado: "Pronto! Vou finalizar automaticamente"
Benefício: Experiência mais fluida - não precisa apertar "finalizar" manualmente.
Função: autoExecuteTransaction()
Descrição: Após 90 dias, se ninguém se pronunciar, favorece automaticamente o vendedor.
Analogia: Como uma regra de futebol:
- Se o jogo não terminar em 90 minutos por decisão
- O juiz apita e define o resultado
- Padrão: vendedor recebe (ele já entregou, presume-se)
Quando acontece:
- ✅ Todos os pagamentos foram feitos
- ❌ Mas não houve consenso nas aprovações
- ⏰ Passaram-se 90 dias desde o prazo
Função: emergencyTimeout()
Descrição: Última proteção contra fundos ficarem presos para sempre.
Analogia: Como chamar o bombeiro:
- Só usa em emergências extremas
- Apenas o "dono do sistema" pode usar
- Depois de 6 meses sem solução
- Salva o dinheiro que ficaria perdido
Situações extremas:
- 💀 Participantes desapareceram
- 🐛 Bug no sistema que ninguém resolve
- 🔥 Disputas eternas que nunca terminam
Função: proposeSettlement(uint256 buyerPercentage, uint256 sellerPercentage)
Descrição: Uma parte propõe dividir o dinheiro sem ir para arbitragem.
Analogia: Como vizinhos que brigaram e decidem conversar:
- "Que tal eu ficar com 70% e você com 30%?"
- "Assim evitamos o tribunal e resolvemos rápido"
- A outra parte tem 30 dias para decidir
Vantagens:
- ⚡ Mais rápido que disputa formal
- 💰 Economiza taxas de arbitragem
- 🎯 Controle total das partes
Função: acceptSettlement()
Descrição: A outra parte aceita a proposta de divisão.
Analogia: "Aceito sua proposta, vamos dividir assim mesmo"
- Automaticamente executa a divisão
- Finaliza o contrato imediatamente
- Todo mundo sai satisfeito
Descrição: Permitem consultar informações sem alterar nada.
Exemplos:
getETHBalance: "Quanto dinheiro tem no cofre?"getRemainingInstallments: "Quantas parcelas faltam?"getEscrowInfo: "Me mostra todos os detalhes desta custódia"
Analogia: Como consultar extrato bancário - você só olha, não mexe em nada.
1. 🏗️ Criar escrow (fora desta seleção)
2. 💎 Depositar garantias
3. 🏁 startEscrow() - Iniciar oficialmente
4. 💰 payInstallmentETH() - Pagar parcelas
5. ✅ setReleaseApproval() - Todos aprovam
6. 🤖 _checkAutoComplete() - Sistema finaliza automaticamente
7. 🏆 withdrawFunds() - Vendedor saca
8. 🎁 returnGuarantee() - Comprador recebe garantia de volta
O que é: Uma metodologia de programação que evita bugs e ataques.
Analogia: Como seguir uma receita de bolo na ordem certa:
- Checks: Conferir se tem todos os ingredientes
- Effects: Misturar tudo na tigela
- Interactions: Só depois colocar no forno
Por que é importante: Se você colocar no forno antes de misturar, dá errado!
O que é: Evita que alguém "fure a fila" e execute funções fora de ordem.
Analogia: Como uma porta giratória que só deixa uma pessoa passar por vez.
- Comprador deposita R$ 50.000 como garantia
- Vendedor deposita o carro como garantia
- Sistema inicia automaticamente
- Comprador paga em parcelas
- Vendedor entrega o carro
- Ambos aprovam a transação
- Sistema libera os fundos
- Comprador alega que o produto não chegou
- Abre disputa no sistema
- Mediador analisa evidências
- Decide: 80% para comprador, 20% para vendedor
- Sistema executa automaticamente
- Escrow: Mecanismo de custódia que retém fundos até condições serem atendidas
- Reentrância: Ataque onde uma função é chamada recursivamente antes de completar
- CEI: Padrão Checks-Effects-Interactions para segurança
- ERC-20: Padrão para tokens fungíveis
- ERC-721: Padrão para NFTs únicos
- ERC-1155: Padrão para tokens colecionáveis
- Garantia: Depósito que assegura cumprimento de obrigações
- Disputa: Conflito entre partes que requer resolução
- Mediador: Terceiro que resolve disputas
- Vesting: Liberação gradual de tokens ao longo do tempo
Este contrato é como um "cofre inteligente" que:
- ✅ Guarda dinheiro com segurança durante negócios
- ✅ Cobra juros automaticamente se alguém atrasar
- ✅ Resolve conflitos quando as partes brigam
- ✅ Finaliza sozinho quando todo mundo concorda
- ✅ Protege contra dinheiro perdido para sempre
- ✅ Permite acordos amigáveis para resolver rápido
Benefício principal: Permite que estranhos façam negócios com segurança, sem precisar confiar uns nos outros! 🤝
- Visão Geral
- Conceitos Fundamentais
- Estrutura do Contrato
- Funções Administrativas
- Criação de Campanhas
- Sistema de Investimento
- Direito de Desistência
- Reembolso e Saques
- Sistema de Líderes
- Sistema de Tokens e Vesting
- Funções Auxiliares
- Exemplos Práticos
- Glossário
Imagine que você está criando uma "Plataforma de Investimento Digital" que funciona como um "Kickstarter Regulado" para investimentos em empresas. Este smart contract implementa as regras da Resolução CVM 88, que é como um "manual de boas práticas" para crowdfunding no Brasil.
Pense no contrato como uma casa de investimentos digital onde:
- 🏢 A Casa: O smart contract
- 📋 Os Projetos: As campanhas de crowdfunding
- 💰 Os Investidores: Pessoas que colocam dinheiro nos projetos
- 👑 Os Líderes: Investidores especiais que recebem comissões
- 🏛️ A CVM: O regulador que define as regras (como um "manual de construção")
struct Campaign {
address creator; // Quem criou a campanha
uint256 minTarget; // Meta mínima (ex: R$ 100.000)
uint256 maxTarget; // Meta máxima (ex: R$ 500.000)
uint256 pledged; // Quanto já foi investido
uint32 startAt; // Quando começa
uint32 endAt; // Quando termina
// ... outros campos
}Analogia: É como um "projeto no Kickstarter" com meta mínima e máxima.
struct Investment {
uint256 amount; // Quanto investiu
bool claimed; // Se já sacou
uint256 investTime; // Quando investiu
// ... outros campos
}Analogia: É como um "recibo de investimento" que guarda todas as informações.
contract CampaignToken is ERC20 {
// Representa o investimento como um token
}Analogia: É como um "certificado de investimento" que você recebe ao investir.
O contrato usa um sistema de "cargos" como uma empresa:
bytes32 public constant INVESTOR_ROLE = keccak256("INVESTOR_ROLE");
bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE");Analogia:
- INVESTOR_ROLE: Como ter uma "carteirinha de investidor"
- CREATOR_ROLE: Como ser um "empreendedor autorizado"
- DEFAULT_ADMIN_ROLE: Como ser o "gerente da casa"
O contrato usa "termômetros digitais" para saber o valor das moedas:
AggregatorV2V3Interface private immutable sequencerUptimeFeed;
AggregatorV2V3Interface private immutable usdcPriceFeed;
AggregatorV2V3Interface private immutable usdtPriceFeed;
AggregatorV2V3Interface private immutable brlPriceFeed;
AggregatorV2V3Interface private immutable ethPriceFeed;Analogia: São como "cotações em tempo real" que você vê no Google Finance, mas automatizadas.
function setAllowedInvestor(address[] memory investors, bool allowed) externalO que faz: Permite ou bloqueia investidores na plataforma.
Analogia: É como "fazer uma lista VIP" de quem pode investir.
Exemplo Prático:
// Permitir que João e Maria possam investir
setAllowedInvestor(["0x123...", "0x456..."], true)function setAllowedCreator(address[] memory creators) externalO que faz: Permite que pessoas criem campanhas.
Analogia: É como "dar permissão para criar projetos" na plataforma.
function setAnnualLimit(uint256 usdLimit) externalO que faz: Define quanto cada investidor pode investir por ano.
Analogia: É como "definir um limite de cartão de crédito" para investimentos.
Exemplo:
- Limite de $10.000 USD por ano
- Convertido automaticamente para BRL
- Protege investidores de investir demais
function launchCampaign(
uint256 _minTarget, // Meta mínima
uint256 _maxTarget, // Meta máxima
uint32 _startAt, // Quando começa
uint32 _endAt, // Quando termina
address _paymentToken, // Qual moeda aceita
address _officialToken, // Token que será distribuído (use address(0) para token global)
address[] memory _leaders, // Líderes da campanha
// ... outros parâmetros
) external returns (uint256)Analogia: É como "criar uma página no Kickstarter" com todas as regras.
O contrato suporta dois modos de token oficial:
-
Token Específico da Campanha:
_officialToken = 0x123... // Token específico para esta campanha
-
Token Global (Fallback):
_officialToken = address(0) // Usa o token definido no constructor
Analogia: É como "escolher entre usar um produto específico ou o padrão da loja".
- Prazo Máximo: Máximo 180 dias (regra CVM)
require(_endAt <= _startAt + MAX_CAMPAIGN_DURATION, "Exceeds 180 days");- Meta Mínima vs Máxima: Mínima deve ser pelo menos 2/3 da máxima
require(_minTarget * 3 >= _maxTarget * 2, "minTarget < 2/3 of maxTarget");- Limite CVM: Máximo 15 milhões de BRL
require(_maxTarget <= MAX_CAMPAIGN_TARGET, "Exceeds 15M CVM limit");// Exemplo 1: Campanha com token específico
launchCampaign(
100_000e18, // Meta mínima: R$ 100.000
300_000e18, // Meta máxima: R$ 300.000
1640995200, // Começa em 1º de janeiro
1648771200, // Termina em 1º de abril (90 dias)
USDC_ADDRESS, // Aceita USDC
TOKEN_ESPECIFICO, // Token específico da empresa
[LEADER1, LEADER2], // 2 líderes
[50_000e6, 30_000e6], // Mínimo de cada líder
[500, 300] // Carry de 5% e 3%
)
// Exemplo 2: Campanha usando token global (fallback)
launchCampaign(
50_000e18, // Meta mínima: R$ 50.000
150_000e18, // Meta máxima: R$ 150.000
1640995200, // Começa em 1º de janeiro
1648771200, // Termina em 1º de abril (90 dias)
USDC_ADDRESS, // Aceita USDC
address(0), // Usa token global definido no constructor
[LEADER1], // 1 líder
[20_000e6], // Mínimo do líder
[300] // Carry de 3%
)function extendDeadline(uint256 _id, uint32 _newEndAt) externalO que faz: Permite estender o prazo da campanha.
Analogia: É como "estender o prazo de uma vaquinha".
Restrições:
- Só o criador pode estender
- Não pode passar de 180 dias
- Novo prazo deve ser maior que o atual
function invest(uint256 _campaignId, uint256 _amount) external payableAnalogia: É como "comprar uma ação" ou "fazer uma doação no Kickstarter".
-
Verificação de Elegibilidade:
require(block.timestamp >= c.startAt, "Not started"); // tempo de interação tem que ser maior ou igual ao do inicio da campanha require(block.timestamp <= c.endAt, "Campaign ended"); // tempo de interação tem que ser menor ou igual ao do final da campanha
-
Cálculo de Limite Anual:
uint256 usdValue = calculateUSDValue(acceptedAmount, c.paymentToken); uint256 brlValue = getBRLPrice(usdValue); require(investedBRLThisYear[msg.sender] + brlValue <= limit, "Exceeds limit"); // o valor já investido pelo endereço de interação + a quantidade que ele está investindo novamente tem que ser menor ou igual ao limite estipulado para os investidores.
-
Registro do Investimento:
inv.amount += acceptedAmount; // trava para evitar que o investidor invista mais que o target e devolve o valor restante até o target da campanha inv.investmentCount++; inv.investmentDates[inv.investmentCount] = block.timestamp;
-
Mint de Tokens:
CampaignToken(c.campaignToken).mint(msg.sender, acceptedAmount);
// João investe 1000 USDC na campanha #1
invest(1, 1000e6) // 1000 USDC com 6 decimais
// O que acontece:
// 1. Verifica se João pode investir
// 2. Calcula valor em BRL (ex: R$ 5.000)
// 3. Verifica limite anual
// 4. Registra investimento
// 5. Minta 1000 tokens de campanha
// 6. Emite evento Invested()function desist(uint256 _campaignId, uint256 _investmentId) externalAnalogia: É como "cancelar uma compra online" dentro do prazo de arrependimento.
require(
block.timestamp <= investment.investmentDates[_investmentId] + DESIST_PERIOD,
"Withdrawal period expired for this investment"
);Exemplo Prático:
- João investe em 1º de janeiro às 10h
- Pode desistir até 6 de janeiro às 10h
- Após esse prazo, não pode mais desistir
- Validação: Verifica se está no prazo
- Cálculo: Pega o valor específico do investimento
- Atualização: Remove da campanha e do investidor
- Devolução: Retorna o dinheiro/token
- Limpeza: Remove o registro do investimento
function claimRefund(uint256 _id) external payableAnalogia: É como "receber o dinheiro de volta" quando um projeto no Kickstarter não atinge a meta.
require(block.timestamp > c.endAt, "Not ended yet");
require(c.pledged < c.minTarget, "Min target reached");
require(inv.amount > 0, "No investment");
require(!inv.claimed, "Already claimed");Exemplo:
- Campanha quer R$ 100.000 (mínimo)
- Só conseguiu R$ 80.000
- Todos os investidores podem sacar o dinheiro de volta
function claimCreator(uint256 _id) external payableAnalogia: É como "o criador do projeto receber o dinheiro" quando atinge a meta.
-
Taxa da Plataforma:
uint256 feeAmount = (totalFunds * c.platformFeeBP) / DIVISOR_FACTOR; // já calcula a taxa do total captado.
-
Carry dos Líderes:
leaderCarryAmounts[i] = (remainingAfterFee * c.leaderCarryBP[i]) / DIVISOR_FACTOR; // calcula a taxa dos lideres sob o valor que restou já retirando taxa da plataforma.
-
Valor para o Criador:
uint256 netAmount = remainingAfterFee - totalCarryAmount; // valor para o criador depois do cálculo de todas as taxas.
// Campanha captou R$ 300.000
// Taxa da plataforma: 5% = R$ 15.000
// Carry do líder: 3% = R$ 8.550
// Criador recebe: R$ 276.450function claimTokens(uint256 _id) externalAnalogia: É como "receber as ações da empresa" após o investimento.
Condições:
- Campanha deve ter terminado
- Deve ter atingido a meta mínima
- Investidor não pode ter sacado antes
function swapForOfficialToken(uint256 _id, uint256 amount) external nonReentrantAnalogia: É como "trocar um voucher por ações da empresa" com liberação gradual.
-
Queima Tokens de Campanha:
CampaignToken(c.campaignToken).burnFrom(msg.sender, amount);
-
Calcula Vesting:
uint256 vestedAmount = calculateVestedAmount(amount, c.vestingStart, c.vestingDuration, block.timestamp);
-
Transfere Tokens Oficiais:
if (vestedAmount > 0) { IERC20(c.officialToken).safeTransfer(msg.sender, vestedAmount); }
Analogia: É como "receber salário com liberação gradual" ao invés de tudo de uma vez.
function calculateVestedAmount(uint256 total, uint32 vestingStart, uint32 vestingDuration, uint256 timestamp)
public pure returns (uint256)
{
if (timestamp < vestingStart) return 0; // Ainda não começou
if (timestamp >= vestingStart + vestingDuration) return total; // Já liberou tudo
return (total * (timestamp - vestingStart)) / vestingDuration; // Liberação proporcional
}Cenário:
- Total de tokens: 1000
- Vesting começa: 1º janeiro 2024
- Duração: 12 meses
- Hoje: 1º abril 2024 (3 meses depois)
Cálculo:
- Tempo decorrido: 3 meses
- Proporção: 3/12 = 25%
- Tokens liberados: 1000 × 25% = 250 tokens
- 🛡️ Proteção: Evita que investidores vendam tudo de uma vez
- 📈 Alinhamento: Mantém investidores interessados no longo prazo
- ⚖️ Equidade: Todos recebem na mesma proporção do tempo
- 🔒 Segurança: Tokens são liberados gradualmente
// João tem 1000 tokens de campanha
// Vesting: 12 meses começando em 1º janeiro
// Hoje: 6 meses depois
swapForOfficialToken(1, 1000) // Queima 1000 tokens de campanha
// Recebe: 500 tokens oficiais (50% do vesting)
// Restante: 500 tokens serão liberados nos próximos 6 mesesAnalogia: São como "investidores âncora" que recebem comissão por trazer outros investidores.
address[] investorLeaders; // Quem são os líderes
bool[] leaderQualified; // Se qualificaram
uint256[] leaderMinContribution; // Quanto precisam investir
uint256[] leaderCarryBP; // Qual carry recebemif (msg.sender == c.investorLeaders[i] && !c.leaderQualified[i]) {
if (inv.amount >= c.leaderMinContribution[i]) {
c.leaderQualified[i] = true;
}
}Exemplo:
- Líder precisa investir mínimo R$ 50.000
- Recebe 5% de carry se qualificar
- Só recebe carry se qualificar
for (uint256 i = 0; i < c.investorLeaders.length; i++) {
if (c.leaderQualified[i] && c.leaderCarryBP[i] > 0) {
leaderCarryAmounts[i] = (remainingAfterFee * c.leaderCarryBP[i]) / DIVISOR_FACTOR;
totalCarryAmount += leaderCarryAmounts[i];
}
}Restrições:
- Máximo 5 líderes por campanha
- Carry total não pode passar de 20%
- Só recebe se qualificar
O contrato usa dois tipos de tokens:
- CampaignToken: Representa o investimento (como um "voucher")
- OfficialToken: Token real da empresa (como "ações")
Analogia: É como ter um "vale-presente" que você pode trocar por "produtos reais" da loja.
Investimento → CampaignToken → Swap → OfficialToken (com vesting)
// 1. João investe 1000 USDC
invest(1, 1000e6)
// Recebe: 1000 CampaignTokens
// 2. Campanha é bem-sucedida
// 3. João pode trocar tokens
swapForOfficialToken(1, 1000)
// Queima: 1000 CampaignTokens
// Recebe: OfficialTokens (com vesting)Analogia: É como "receber salário com liberação gradual" ao invés de tudo de uma vez.
function calculateVestedAmount(uint256 total, uint32 vestingStart, uint32 vestingDuration, uint256 timestamp)
public pure returns (uint256)
{
if (timestamp < vestingStart) return 0; // Ainda não começou
if (timestamp >= vestingStart + vestingDuration) return total; // Já liberou tudo
return (total * (timestamp - vestingStart)) / vestingDuration; // Liberação proporcional
}Cenário 1 - Vesting de 12 meses:
- Total: 1000 tokens
- Vesting: 1º janeiro a 31º dezembro
- Hoje: 1º abril (3 meses)
- Liberado: 1000 × (3/12) = 250 tokens
Cenário 2 - Vesting de 6 meses:
- Total: 1000 tokens
- Vesting: 1º janeiro a 30º junho
- Hoje: 1º março (2 meses)
- Liberado: 1000 × (2/6) = 333 tokens
- 🛡️ Proteção: Evita que investidores vendam tudo de uma vez
- 📈 Alinhamento: Mantém investidores interessados no longo prazo
- ⚖️ Equidade: Todos recebem na mesma proporção do tempo
- 🔒 Segurança: Tokens são liberados gradualmente
- 📊 Transparência: Cálculo matemático simples e previsível
- Vesting: 24 meses
- Objetivo: Manter investidores alinhados com crescimento
- Resultado: Liberação gradual conforme empresa cresce
- Vesting: 12 meses
- Objetivo: Estabilidade financeira
- Resultado: Receita previsível ao longo do ano
- Vesting: 18 meses
- Objetivo: Alinhar com ciclo de produção
- Resultado: Tokens liberados conforme produção aumenta
function calculateUSDValue(uint256 amount, address token) internal view returns (uint256)Analogia: É como "converter moedas" usando cotações em tempo real.
Exemplo:
- 1000 USDC = $1000 USD
- 1 ETH = $3000 USD
- 1000 USDT = $1000 USD
function getBRLPrice(uint256 usdAmount) public view returns (uint256)Analogia: É como "converter dólar para real" usando cotação atual.
Exemplo:
-
$1000 USD = R$ 5000 BRL (cotação 1 USD = 5 BRL)
function validateCampaignAmount(uint256 amount, address token) internal viewO que faz: Verifica se o valor não excede o limite da CVM.
Analogia: É como "verificar se não está ultrapassando o limite de cartão".
function checkSequencer() internal viewO que faz: Verifica se os oráculos estão funcionando.
Analogia: É como "verificar se o termômetro está funcionando" antes de medir a temperatura.
function hasExpiredInvestments(uint256 _campaignId, address _investor) public view returns (bool)O que faz: Verifica se algum investimento do investidor já passou do período de 5 dias.
Analogia: É como "verificar se algum produto já passou da data de validade".
for (uint256 i = 1; i <= investment.investmentCount; i++) {
if (block.timestamp > investment.investmentDates[i] + 5 days) {
return true; // Tem investimento expirado
}
}
return false; // Nenhum investimento expirado// João fez 3 investimentos:
// 1º: 1º janeiro (já passou de 5 dias)
// 2º: 5º janeiro (já passou de 5 dias)
// 3º: 10º janeiro (ainda dentro de 5 dias)
hasExpiredInvestments(1, "0xJoão") // Retorna: true
// Porque tem investimentos que já passaram de 5 diasCenário: Uma startup quer captar R$ 200.000 a R$ 500.000
// 1. Criar campanha
launchCampaign(
200_000e18, // Mínimo: R$ 200.000
500_000e18, // Máximo: R$ 500.000
1640995200, // Início: 1º jan 2022
1648771200, // Fim: 1º abr 2022 (90 dias)
USDC_ADDRESS, // Aceita USDC
TOKEN_ADDRESS, // Distribui tokens
[LEADER1], // 1 líder
[50_000e6], // Líder investe mínimo 50.000 USDC
[500] // Líder recebe 5% carry
)
// 2. Investidores fazem aportes
invest(1, 10_000e6) // João investe 10.000 USDC
invest(1, 5_000e6) // Maria investe 5.000 USDC
invest(1, 50_000e6) // Líder investe 50.000 USDC (qualifica)
// 3. Campanha atinge meta
// 4. Criador saca fundos
claimCreator(1)
// 5. Investidores recebem tokens
claimTokens(1)Cenário: Hospital quer captar R$ 1.000.000 a R$ 2.000.000
// Campanha com 3 líderes
launchCampaign(
1_000_000e18, // Mínimo: R$ 1M
2_000_000e18, // Máximo: R$ 2M
1640995200, // 90 dias
1648771200,
USDC_ADDRESS,
TOKEN_ADDRESS,
[LEADER1, LEADER2, LEADER3], // 3 líderes
[100_000e6, 80_000e6, 60_000e6], // Mínimos
[800, 600, 400] // Carry: 8%, 6%, 4%
)- Basis Points (BP): 1/100 de 1%. Ex: 100 BP = 1%
- Vesting: Liberação gradual de tokens ao longo do tempo
- Carry: Comissão extra para líderes de investimento
- Oráculo: Fonte de dados externa (preços, etc.)
- ReentrancyGuard: Proteção contra ataques de reentrada
- SafeERC20: Biblioteca segura para transferências de tokens
- Prazo Máximo: 180 dias para campanhas
- Limite Máximo: 15 milhões de BRL por campanha
- Período de Desistência: 5 dias para arrependimento
- Limite Anual: Controle de quanto cada investidor pode investir por ano
- Min Target: Meta mínima para campanha ser bem-sucedida
- Max Target: Meta máxima que pode ser captada
- Pledged: Valor total já investido
- Platform Fee: Taxa cobrada pela plataforma
- Carry: Comissão para líderes de investimento
Este smart contract implementa um sistema completo de crowdfunding regulado que:
- ✅ Segue as regras da CVM 88
- 💰 Aceita múltiplas moedas (ETH, USDC, USDT)
- 👑 Suporta sistema de líderes com carry
- ⏰ Implementa direito de desistência
- 🔒 Usa oráculos para preços seguros
- 📊 Controla limites anuais de investimento
É como um "Kickstarter profissional" com todas as proteções regulatórias necessárias para o mercado brasileiro! 🚀
Antes de fazer o deploy, você precisa configurar as variáveis de ambiente:
# .env
PRIVATE_KEY=sua_chave_privada_aqui_sem_0x
BASE_RPC_URL=https://mainnet.base.org
AMOY_RPC_URL="https://amoy.g.alchemy.com/v2/"
ETHERSCAN_API_KEY=sua_api_key_do_etherscanforge script script/Token.s.sol:TokenScript \
--rpc-url https://mainnet.base.org \
--private-key ${PRIVATE_KEY} \
--broadcast \
--verify \
--etherscan-api-key ${ETHERSCAN_API_KEY} \
--verifier-url https://api.basescan.org/apiforge script script/Token.s.sol:TokenScript \
--rpc-url https://sepolia.base.org \
--private-key ${PRIVATE_KEY} \
--broadcast \
--verify \
--etherscan-api-key ${ETHERSCAN_API_KEY} \
--verifier-url https://api-sepolia.basescan.org/apiforge script script/Token.s.sol:TokenScript \
--rpc-url https://amoy.base.org \
--private-key ${PRIVATE_KEY} \
--broadcast \
--verify \
--etherscan-api-key ${POLYGONSCAN_API_KEY} \
--verifier-url https://api-amoy.polygonscan.com/api# Simular deploy sem executar
forge script script/Token.s.sol:TokenScript \
--rpc-url ${BASE_RPC_URL} \
--private-key ${PRIVATE_KEY} \
--dry-run
# Simular com logs detalhados
forge script script/Token.s.sol:TokenScript \
--rpc-url ${BASE_RPC_URL} \
--private-key ${PRIVATE_KEY} \
--dry-run \
-vvvvSe precisar verificar manualmente após o deploy:
# Verificar na Base Mainnet
forge verify-contract \
ENDERECO_DO_CONTRATO \
src/Token.sol:Token \
--chain-id 8453 \
--etherscan-api-key ${ETHERSCAN_API_KEY} \
--verifier-url https://api.basescan.org/api
# Verificar na Base Sepolia
forge verify-contract \
ENDERECO_DO_CONTRATO \
src/Token.sol:Token \
--chain-id 84532 \
--etherscan-api-key ${ETHERSCAN_API_KEY} \
--verifier-url https://api-sepolia.basescan.org/api
## Verificar na Amoy
forge verify-contract \
ENDERECO_DO_CONTRATO \
src/Token.sol:Token \
--chain-id 80002 \
--etherscan-api-key ${POLYGONSCAN_API_KEY} \
--verifier-url https://api-amoy.polygonscan.com/api# Compilar contratos
forge build
# Compilar com força (limpar cache)
forge build --force
# Verificar tamanho dos contratos
forge build --sizes
# Executar testes
forge test
# Executar testes com logs
forge test -vvv# Verificar status da transação
cast tx-status HASH_DA_TRANSACAO --rpc-url ${BASE_RPC_URL}
# Verificar logs do contrato
cast logs ENDERECO_DO_CONTRATO --rpc-url ${BASE_RPC_URL}
# Consultar saldo de ETH
cast balance ENDERECO --rpc-url ${BASE_RPC_URL}# ✅ 1. Verificar variáveis de ambiente
echo $PRIVATE_KEY
echo $BASE_RPC_URL
echo $ETHERSCAN_API_KEY
# ✅ 2. Compilar contratos
forge build
# ✅ 3. Testar localmente
forge test
# ✅ 4. Simular deploy
forge script script/Token.s.sol:TokenScript \
--rpc-url ${BASE_RPC_URL} \
--private-key ${PRIVATE_KEY} \
--dry-run
# ✅ 5. Deploy real com verificação
forge script script/Token.s.sol:TokenScript \
--rpc-url ${BASE_RPC_URL} \
--private-key ${PRIVATE_KEY} \
--broadcast \
--verify \
--etherscan-api-key ${ETHERSCAN_API_KEY} \
--verifier-url https://api.basescan.org/api# Deploy completo com verificação
forge script script/Token.s.sol:TokenScript \
--rpc-url https://mainnet.base.org \
--private-key 0x1234567890abcdef... \
--broadcast \
--verify \
--etherscan-api-key ABC123DEF456... \
--verifier-url https://api.basescan.org/api \
-vvvvO projeto já está configurado com:
[rpc_endpoints]
base = "${BASE_RPC_URL}"
amoy = "${AMOY_RPC_URL}"
[etherscan]
base = { key = "${ETHERSCAN_API_KEY}" }
amoy = { key = "${POLYGONSCAN_API_KEY}" }- RPC URL:
https://mainnet.base.org - Chain ID:
8453 - Explorer:
https://basescan.org - Verifier URL:
https://api.basescan.org/api
- RPC URL:
https://sepolia.base.org - Chain ID:
84532 - Explorer:
https://sepolia.basescan.org - Verifier URL:
https://api-sepolia.basescan.org/api
# Verificar se a chave privada está correta
echo $PRIVATE_KEY | wc -c
# Deve retornar 65 (32 bytes + 1 para o '0x')# Verificar saldo na Base
cast balance $(cast wallet address) --rpc-url ${BASE_RPC_URL}# Tentar verificação manual
forge verify-contract ENDERECO src/Token.sol:Token \
--chain-id 8453 \
--etherscan-api-key ${ETHERSCAN_API_KEY}- 🔐 Segurança: Nunca compartilhe sua chave privada
- 💰 Gas: Mantenha ETH suficiente para gas fees
- 📝 Logs: Use
-vvvvpara logs detalhados - 🧪 Testnet: Sempre teste na Sepolia primeiro
- 🔍 Verificação: Sempre verifique o contrato após deploy
Após o deploy bem-sucedido, você terá:
- ✅ Contrato deployado na Base
- ✅ Código verificado no Basescan
- ✅ Contrato pronto para uso
- ✅ Documentação completa disponível