Skip to content

Repository files navigation

FIDUCIA

Agentic RWA index underwriter for tokenised equities on Robinhood Chain.

A mandate is a machine-readable object, not a PDF. It is validated, canonicalised and hashed, so the index it describes has a cryptographic identity. Rebalances are planned deterministically off-chain, committed as a hash, and only then executed — the vault refuses any leg set that does not match what was committed.

Status, plainly

This repository is a protocol design plus a reference implementation. Being straight about what is and is not real:

Component State
packages/mandate — deterministic core Runs. 46 tests passing. Validation, RFC 8785 canonicalisation, weight solving, integer NAV/drift, planHash
packages/contracts — Solidity Written, complete. Not deployed. No Solidity tests yet
packages/db — Postgres schema Written: 12 tables via Drizzle. Migration chain incomplete
apps/api — Fastify service Partial. 3 of 9 route modules present
apps/agents — 5 ACP agents Written against an AcpPort interface. No ACP adapter exists, so no agent runs
apps/web — dashboard Scaffolding fragments. Does not render
landing — marketing site Live at fiduciaindex.com

The $FIDUCIA token is live on Virtuals. No token utility is implemented in this repo — all protocol economics are denominated in USDG. Nothing here is deployed to mainnet, and no ACP job has been created or settled.

The part that works

packages/mandate is self-contained and has no dependency beyond viem and zod. It is the piece the on-chain guard depends on.

cd packages/mandate
pnpm install
node --experimental-strip-types --test test/*.test.ts     # 46 tests
node --experimental-strip-types examples/deterministic-plan.ts

The example walks a full cycle on fixed input — no network, no keys, no RPC — so it produces identical output on any machine:

1. MANDATE IDENTITY      mandateHash 0xe3420dcc…  (827 canonical bytes)
2. TARGET WEIGHTS        NVDA raw 29.05% -> 24.53%, cap binds, sum exactly 10000 bps
3. CURRENT BOOK          NAV 10000 USDG, NVDA +11.47% off target
4. REBALANCE PLAN        drift 1147 -> 0 bps, turnover 2294 bps, 5 legs
                         planHash 0x07061e2f…
5. DETERMINISM           100 rebuilds -> identical planHash
6. TAMPER DETECTION      halve one minAmountOut -> 0x76c1fff7… (vault reverts)
                         swap one calldata blob -> 0xfbccc8be… (vault reverts)

How the guard works

The interesting property is that a keeper is trusted to submit a rebalance but never trusted to choose it.

mandate document
  │  validate (Zod)  ->  canonicalise (RFC 8785 JCS)  ->  keccak256
  ▼
mandateHash ────────────────────────────► MandateRegistry (append-only)
                                          IndexFactory CREATE2 salt
holdings + oracle prices + signals
  │  solveWeights   cap/floor water-fill, sums to exactly 10000 bps
  │  computeNav     integer only, no floats anywhere near money
  │  buildPlan      minimum notional that clears the drift band
  ▼
legs[] ──► planHash = keccak256(abi.encode(legs))
                │
                ├─ commitRebalance(planHash)          block N
                │
                └─ executeRebalance(legs)             block N+1
                     recomputes the hash and reverts PlanMismatch on any diff

Because swapData is inside the hash rather than passed alongside it, a keeper cannot commit one plan and execute another with substituted router calldata.

IndexVault adds three further checks that do not depend on trusting the keeper: drift must strictly decrease (driftAfter > driftBefore reverts), slippage is checked against the oracle as well as minAmountOut (which a lying keeper could otherwise set to zero), and settlement requires an independent evaluator's EIP-712 attestation.

Layout

packages/mandate      deterministic core — validation, canonicalisation, planning
packages/contracts    IndexVault, IndexFactory, MandateRegistry, NavOracle,
                      AttestationHub, StockTokenRegistry
packages/db           Drizzle schema
packages/chain        chain clients and ports (AcpPort has no adapter yet)
apps/api              Fastify API
apps/agents           underwriter, evaluator, rebalancer, indexer, fee
apps/web              dashboard (incomplete)
landing               marketing site — live

Design notes worth knowing

Integer arithmetic throughout. NAV, drift and every leg amount are bigint. The Postgres schema uses a custom numeric(78,0) type rather than a float column. Money never touches IEEE 754.

Canonicalisation is not cosmetic. Two byte-different but semantically identical mandates must hash identically, or an index becomes unaddressable by its own author. Addresses are lowercased and schema defaults are materialised before hashing; both are covered by tests.

Minimum-notional sizing. A rebalance trades the least amount that brings every name inside the drift band, rather than moving everything to target. Lower turnover, same mandate compliance.

Contributing

The honest priority list: an AcpPort adapter so the agents can actually run, Foundry tests plus a deployment to Robinhood Chain, and a cross-language check that TypeScript planHash and Solidity keccak256(abi.encode(legs)) agree on identical bytes. The tuple order is written to match and is covered by a test against an independent encoding, but it has never been verified against a live contract.

Licence

MIT

About

Agentic RWA index underwriter for tokenised equities on Robinhood Chain. Mandates are hashed objects; rebalances are deterministically planned, hash-committed, then executed.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages