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.
> 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, snapshotsOs 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:
# 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:
# 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:
# 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 cedenteFase 3 — Pagamentos e inadimplência
Os devedores pagam na conta-vinculada:
# 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 parcialFase 4 — Waterfall e cotas
O gestor executa a distribuição periódica:
# 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:
# 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 normalmenteEmissão de cotas:
# 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
# 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 paraBreached+ emiteCovenantBreached- O
FidcWaterfallbloqueia a distribuição para cotas subordinadas - Novas aquisições devem ser bloqueadas pelo pool (responsabilidade do gestor)
Restauração exige recapitalização confirmada:
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 subordinadoElegibilidade 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
# 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_id2. Chamada cross-contract via ContractRef
# 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:
# 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:
let total = self.senior_pnl + self.subordinated_pnl
let numerator = self.subordinated_pnl * 10000
let ratio = numerator / total # IS em bps5. Strings em emit sem vírgulas
O parser TREA trata vírgulas como separadores de argumentos dentro de emit:
# 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
- Implante um novo contrato que implementa
IFidcEligibilityPolicy - Chame
pool.set_eligibility_policy(new_policy_addr)(admin) - O pool passará a usar a nova política sem alterar os recebíveis existentes
Adicionar nova classe de cota
- Implante um
FidcTrancheTokencomtranche_class = "ClasseEspecial" - Configure um novo vault no
FidcWaterfall.configure_tranches()(se implementado) - 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 | ✅ |