Voltar para Documentação

Docs Técnicas

FIDC OverPower — Fundo de Investimento em Direitos Creditórios

> Referência normativa: Resolução CVM 175 e Anexo Normativo II – FIDC. > A modelagem abaixo é uma implementação de referência em TREA. Validação jurídica > é necessária antes de qualquer uso em ambiente regulado.

O conteúdo abaixo vem das fontes técnicas do repositório e é prerenderizado no site para leitura direta por pessoas, crawlers e agentes.

> Referência normativa: Resolução CVM 175 e Anexo Normativo II – FIDC. > A modelagem abaixo é uma implementação de referência em TREA. Validação jurídica > é necessária antes de qualquer uso em ambiente regulado.

O que é um FIDC

Um Fundo de Investimento em Direitos Creditórios (FIDC) é um veículo de securitização que adquire recebíveis (duplicatas, contratos de crédito, boletos) de empresas cedentes e os transforma em cotas para investidores. O fundo paga ao cedente pelo fluxo de caixa futuro dos devedores.

A suíte FIDC OverPower modela esse produto completo em TREA: desde a proposta de um recebível até o waterfall de distribuição para cotistas e a auditoria periódica da carteira.

Arquitetura — 10 contratos, uma suíte coerente

FidcCreditRight          ← stdlib: modelo canônico do recebível
FidcEligibilityPolicy    ← stdlib: motor de validação e limites
─────────────────────────────────────────────────────────────
FidcPool                 ← raiz: registra e governa o fundo
FidcAssignmentAgreement  ← cessão: lote de recebíveis, preço travado
FidcCollectionsVault     ← conta-vinculada: caixa dos devedores
FidcDefaultAndRecovery   ← inadimplência e recuperação
FidcSubordinationMonitor ← covenants de IS (índice de subordinação)
FidcWaterfall            ← distribuição por prioridade de classe
FidcTrancheToken         ← cotas permissionadas por classe
FidcAuditDataroom        ← hashes de lastro, atestações, snapshots

Os contratos stdlib (FidcCreditRight, FidcEligibilityPolicy) são implantados individualmente para cada recebível/fundo e importados como módulos. Os contratos products são implantados uma vez por fundo/classe.

Grafo de dependências (em tempo de execução)

FidcPool ──────→ FidcEligibilityPolicy   (aceita/rejeita recebível)
         ──────→ FidcCreditRight          (via ContractRef por cessão)
         ──────→ FidcAssignmentAgreement  (lote de cessão)
         ──────→ FidcDefaultAndRecovery   (inadimplência)
         ──────→ FidcCollectionsVault     (caixa)
         ──────→ FidcSubordinationMonitor (covenant IS)
         ──────→ FidcWaterfall            (distribuição)
         ──────→ FidcAuditDataroom        (auditoria)

FidcWaterfall ──→ FidcSubordinationMonitor  (is_breached() antes de pagar sub)
FidcCollectionsVault ──→ FidcCreditRight    (allocate_payment() por recebível)
FidcAssignmentAgreement ──→ FidcCreditRight (assign() + mark_funded())

Lifecycle completo — do recebível ao cotista

Fase 1 — Proposta e elegibilidade

O cedente propõe um recebível criando um contrato FidcCreditRight filho:

trea
# O endereço do contrato filho é derivado deterministicamente
let expected_addr = derive_child_id(self.fund_ref, cedente_addr, credit_right_id)

O pool valida via FidcEligibilityPolicy:

trea
# FidcEligibilityPolicy verifica:
# – lastro hash presente
# – devedor não em lista restrita
# – prazo dentro do máximo configurado
# – concentração por devedor/cedente dentro do limite
self.eligibility_policy.accept(credit_right_id, cedente_addr, debtor_addr,
                                face_value, maturity_date, non_standard)

Se aprovado, o pool chama cr_ref.mark_eligible() no contrato filho.

Fase 2 — Cessão e funding

O gestor cria um FidcAssignmentAgreement com o lote aprovado:

trea
# Cedente adiciona recebíveis elegíveis ao lote
agreement.add("CR-001", 95_000)   # preço de aquisição
agreement.add("CR-002", 48_000)

# Cedente confirma o lote (bloqueia edição)
agreement.confirm()

# Pool executa cada item via ContractRef
agreement.execute_one("CR-001", cr_ref_001)
agreement.execute_one("CR-002", cr_ref_002)

# Liquida: vault do fundo → cedente
agreement.settle()   # ctx.transfer: 143_000 BRL ao cedente

Fase 3 — Pagamentos e inadimplência

Os devedores pagam na conta-vinculada:

trea
# Pagamento identificado: debtor → fund_vault, allocate no CR
vault.receive_payment("CR-001", cr_ref, debtor_addr, 10_000)

# Pagamento não identificado: fica retido até reconciliação
vault.hold_unmatched_payment("REF-XYZ", source_addr, 5_000)
vault.reconcile_unmatched("REF-XYZ", "CR-002", cr_ref)

# Inadimplência
default_and_recovery.register_default("CR-003", cr_ref)  # marca CR-003 como Defaulted
default_and_recovery.register_recovery("CR-003", cr_ref, 2_000)  # recuperação parcial

Fase 4 — Waterfall e cotas

O gestor executa a distribuição periódica:

trea
# Antes de executar, simula para auditoria prévia
waterfall.simulate("2026-06", 5_000, 80_000, 15_000, 20_000, 3_000)
# → emite WaterfallSimulated com is_breached=false, total=123_000

# Executa o waterfall completo
# 1. Despesas: 5_000 → expense_recipient
# 2. Sênior:  80_000 → senior_vault
# 3. Mezanino: 15_000 → mezzanine_vault
# 4. Subordinado: 20_000 → subordinated_vault (BLOQUEADO se IS < mínimo)
# 5. Residual: 3_000 → residual_recipient
waterfall.execute("2026-06", 5_000, 80_000, 15_000, 20_000, 3_000)

Subordinação bloqueada em breach:

trea
# FidcSubordinationMonitor calcula IS = PL_sub×10000 / (PL_sen+PL_sub)
monitor.update_quota_values(8_000_000, 1_500_000)  # IS = 1578 bps
monitor.evaluate()
# → emite SubordinationBreach (IS=1578 < min=2000)

# Waterfall consulta is_breached() antes de pagar subordinado
# → emite SubordinationDistributionBlocked
# → residual ainda é distribuído normalmente

Emissão de cotas:

trea
# Admin whitelista investidores e emite cotas
senior_tranche.whitelist_investor("wallet:fundo-pensao-xp")
senior_tranche.issue("wallet:fundo-pensao-xp", 800_000)

subordinated_tranche.whitelist_investor("wallet:cedente-originador")
subordinated_tranche.issue("wallet:cedente-originador", 200_000)

# Transferência entre investidores whitelistados
senior_tranche.transfer("wallet:fundo-pensao-xp", "wallet:fundo-pensao-bco", 100_000)

Fase 5 — Auditoria e conformidade

trea
# Registra regulamento com hash imutável
dataroom.register_policy_version("sha256:abc...", "Versão aprovada em AGC 2026-03-01")

# Lastro de cada recebível registrado com hash de documento
dataroom.register_document("doc:cr-001", "sha256:def...", "NotaFiscalEletronica")
dataroom.register_document("doc:cr-002", "sha256:ghi...", "CertidaoCessao")

# Entidade registradora atesta na cadeia
dataroom.attest_registry("doc:cr-001", "B3-REG-2026-001", "Registro confirmado")

# Custodiante atesta o portfólio mensalmente
dataroom.attest_custody("2026-06", "sha256:portfolio-hash", "Carteira confere com CETIP")

# Auditor publica snapshot regulatório
dataroom.publish_portfolio_snapshot("2026-06", 93, 5_000_000, 2, 2000)

# Auditor emite recibo institucional exportável
dataroom.emit_audit_receipt(
    "2026-06",
    "PeriodicAudit",
    "93 recebíveis auditados. IS=20%. 2 inadimplências registradas. Conforme CVM 175."
)

Modelo de eventos regulatórios

Cada decisão relevante emite um evento com linguagem de produto:

| Evento | Contrato | Significado | |--------|----------|-------------| | EligibilityEvaluated | FidcEligibilityPolicy | Recebível aceito/rejeitado com reason_code | | AssignmentSettled | FidcAssignmentAgreement | Cessão liquidada: total pago ao cedente | | DebtorPaymentReceived | FidcCollectionsVault | Pagamento do devedor reconciliado | | UnmatchedPaymentHeld | FidcCollectionsVault | Pagamento retido aguardando identificação | | CashReleasedToWaterfall | FidcCollectionsVault | Caixa liberado para distribuição | | CovenantBreached | FidcSubordinationMonitor | IS caiu abaixo do mínimo | | CovenantRestored | FidcSubordinationMonitor | IS restaurado após remedição | | SubordinationDistributionBlocked | FidcWaterfall | Cota subordinada não distribuída por breach | | TranchePaid | FidcWaterfall | Classe de cota recebeu distribuição | | ResidualAllocated | FidcWaterfall | Excess distribuído ao residual_recipient | | CreditRightDefaulted | FidcCreditRight | Recebível marcado como inadimplente | | DocumentHashRegistered | FidcAuditDataroom | Hash de lastro registrado em cadeia | | AuditReceiptEmitted | FidcAuditDataroom | Recibo de auditoria exportável emitido |

Modelo de risco e covenants

Índice de Subordinação (IS)

IS = PL_subordinado × 10000 / (PL_senior + PL_subordinado)    [bps]

O FidcSubordinationMonitor avalia o IS após cada evento relevante. Se IS < min_subordination_bps:

  • evaluate() → transita para Breached + emite CovenantBreached
  • O FidcWaterfall bloqueia a distribuição para cotas subordinadas
  • Novas aquisições devem ser bloqueadas pelo pool (responsabilidade do gestor)

Restauração exige recapitalização confirmada:

trea
monitor.update_quota_values(7_000_000, 3_000_000)  # IS agora = 3000 bps
monitor.restore("Emissão de cotas subordinadas aprovada em AGC")
# → emite CovenantRestored; waterfall volta a pagar subordinado

Elegibilidade e concentração

A FidcEligibilityPolicy aplica regras configuráveis:

  • Valor mínimo/máximo por recebível
  • Prazo máximo até vencimento
  • Concentração máxima por devedor (em bps do PL)
  • Concentração máxima por cedente
  • Lista restrita de devedores/cedentes
  • Flag para direitos creditórios não-padronizados

Cada avaliação emite EligibilityEvaluated com accepted, reason_code e policy_version.

Waterfall — ordem de prioridade

Caixa disponível na conta-vinculada
           │
     ┌─────▼──────┐
     │  Despesas  │ ← taxa admin, custódia, serviços autorizados
     └─────┬──────┘
           │
     ┌─────▼──────┐
     │   Sênior   │ ← amortização + rendimento; prioridade máxima
     └─────┬──────┘
           │
     ┌─────▼──────┐
     │  Mezanino  │ ← opcional; zero_address desativa o slot
     └─────┬──────┘
           │
     ┌─────▼──────────────────────────────┐
     │  Subordinado  [BLOQUEADO se breach] │ ← absorve perdas primeiro
     └─────┬──────────────────────────────┘
           │
     ┌─────▼──────┐
     │  Residual  │ ← excess após todas as classes → residual_recipient
     └────────────┘

O gestor calcula os valores off-chain e passa para execute(). A soma deve corresponder ao saldo disponível em collections_vault. O contrato valida autoridade e estado do monitor; não valida saldo (responsabilidade do gestor/custodian).

Padrões de implementação TREA usados nesta suíte

1. Contratos filhos com endereço determinístico

trea
# FidcPool registra recebíveis como contratos filhos
let expected = derive_child_id(self.fund_ref, cedente_addr, credit_right_id)
require(cr_contract_id == expected, "child_address_mismatch")
self.credit_rights[credit_right_id] = cr_contract_id

2. Chamada cross-contract via ContractRef

trea
# FidcWaterfall: consulta monitor antes de pagar subordinado
interface IFidcSubordinationMonitor:
    @view
    def is_breached() -> bool

# Em storage:
subordination_monitor: ContractRef[IFidcSubordinationMonitor]

# Em execute():
let breached = self.subordination_monitor.is_breached()

3. Map[String, u128] para flags booleanas

O runtime TREA retorna Integer(0) como default de Maps independente do tipo declarado. Para flags booleanas, use u128 com convenção 0=false, 1=true:

trea
# CORRETO
doc_registered: Map[String, u128, 2048]
require(self.doc_registered[doc_id] == 0, "already_registered")
self.doc_registered[doc_id] = 1

# INCORRETO (causa bugs sutis)
# doc_registered: Map[String, bool, 2048]  ← map_get retorna Integer(0), não Bool(false)

4. Cálculo de IS sem parênteses aninhados

TREA não suporta sub-expressões parentetizadas em atribuições. Use variáveis intermediárias:

trea
let total = self.senior_pnl + self.subordinated_pnl
let numerator = self.subordinated_pnl * 10000
let ratio = numerator / total   # IS em bps

5. Strings em emit sem vírgulas

O parser TREA trata vírgulas como separadores de argumentos dentro de emit:

trea
# INCORRETO
emit Event("Recebível avaliado, filho marcado elegível.")

# CORRETO
emit Event("Recebivel avaliado e filho marcado elegivel.")

Guia de extensibilidade

Adicionar nova política de elegibilidade

  1. Implante um novo contrato que implementa IFidcEligibilityPolicy
  2. Chame pool.set_eligibility_policy(new_policy_addr) (admin)
  3. O pool passará a usar a nova política sem alterar os recebíveis existentes

Adicionar nova classe de cota

  1. Implante um FidcTrancheToken com tranche_class = "ClasseEspecial"
  2. Configure um novo vault no FidcWaterfall.configure_tranches() (se implementado)
  3. Atualize o regulamento do fundo e registre a nova versão no FidcAuditDataroom

Suporte a múltiplos fundos/classes

Cada fundo é um conjunto isolado de contratos. O fund_ref é a âncora comum — todos os contratos de um fundo usam o mesmo fund_ref (ex: "FIDC-XP-2026-I").

Critérios de aceite (issue #67)

| Critério | Status | |----------|--------| | Templates TREA para pool, credit right, eligibility, assignment, vault, waterfall, tranches | ✅ | | Nenhum contrato usa Map aninhado | ✅ | | Receipts institucionais emitidos para decisões materiais | ✅ | | E2E cobre aquisição, pagamento, waterfall, default, recuperação, auditoria | ✅ | | TREA Book documenta o fluxo completo | ✅ | | cargo fmt --check e testes passam | ✅ |