Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions showcase/fiducia-deterministic-rebalance/PROOF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Proof — Fiducia deterministic rebalance planner

Everything below is reproducible from a clean clone. The planner is pure
computation: no RPC, no keys, no network, no clock. Same input, same hash,
anywhere.

```bash
git clone https://github.com/fiduciaindex/fiducia.git
cd fiducia/packages/mandate
pnpm install --ignore-workspace

node --experimental-strip-types --test test/*.test.ts # 46 tests
node --experimental-strip-types examples/deterministic-plan.ts
```

Node 22 or newer. `--experimental-strip-types` runs the TypeScript directly, so
there is no build step to trust.

## What the demo shows

A five-name mandate (cap 30%, floor 10%, USDG base) against a book that has
drifted, planned into a leg sequence and committed to a single hash.

```
========================================================================
1. MANDATE IDENTITY
========================================================================
name : Mag Five Capped
canonical : {"base":{"address":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","dec...
bytes : 827
mandateHash : 0xe3420dccd9f0f958d51e2b2e1ae6d3a6fac1baffe12dc23e9a7a213e2d5125dd

========================================================================
2. TARGET WEIGHTS (cap 30%, floor 10%)
========================================================================
AAPL raw 22.97% target 21.49%
MSFT raw 20.95% target 20.47%
NVDA raw 29.05% target 24.53%
AMZN raw 12.84% target 16.42%
GOOGL raw 14.19% target 17.09%
sum 10000 bps (must be exactly 10000)

========================================================================
3. CURRENT BOOK
========================================================================
NAV : 10000 USDG
AAPL 18.00% vs target -3.49%
MSFT 20.00% vs target -0.47%
NVDA 36.00% vs target +11.47%
AMZN 9.00% vs target -7.42%
GOOGL 17.00% vs target -0.09%

========================================================================
4. REBALANCE PLAN
========================================================================
drift before : 1147 bps
drift expected : 0 bps
turnover : 2294 bps (cap 3000 bps)
violations : none

seq side ticker notional minAmountOut
0 SELL NVDA 1147 USDG 1141
1 BUY AMZN 742 USDG 738
2 BUY AAPL 349 USDG 347
3 BUY MSFT 47 USDG 46
4 BUY GOOGL 9 USDG 8

planHash : 0x07061e2f22ea83cca89468e9eba86c26300e92896a0e1ce69a8d30187df3e30e

========================================================================
5. DETERMINISM
========================================================================
100 rebuilds -> identical planHash

========================================================================
6. TAMPER DETECTION
========================================================================
committed : 0x07061e2f22ea83cca89468e9eba86c26300e92896a0e1ce69a8d30187df3e30e
halve minAmountOut: 0x76c1fff76e9629e20d6a9eae65cf28e98def3024ced63bc5e1ffb073496fc4af
-> IndexVault reverts PlanMismatch

swap calldata swapped: 0xfbccc8be085024cb1c881fe72c89834955adabd000af0f842aefcf1607cd8040
-> also reverts: calldata is inside the hash, not alongside it
```

## Why the hashes are the point

An agent that plans its own trades can quietly widen its own limits. The plan is
hashed before execution and checked at settlement, so the discretion is bounded
by arithmetic rather than by trust:

- Sells are sequenced before buys, so the base currency exists before it is spent.
- `minAmountOut` is `notional * 9950 / 10000` — halving it changes `planHash`.
- Swap calldata is hashed *inside* the tuple, not passed alongside it, so
swapping the router payload changes `planHash` too.
- Section 6 demonstrates both tamper cases producing different hashes.

The hash preimage is an ABI encoding of `(bool, bytes32, uint256, uint256,
address, bytes)[]`, matching what the Solidity side would hash.
`test/planner.test.ts` asserts this against an independent restatement of the
tuple rather than against the implementation's own helper.

## A real bug this surfaced

The weight solver carried a comment claiming its invariants were
"property-tested". No tests existed. Writing them found that `waterFill`
returned allocations summing to **13000 bps — 130%** whenever a cap and a floor
bound simultaneously. With `cap 0.9 / floor 0.1` over five assets it produced
`0.9 + 4×0.1` instead of the feasible `0.6 + 4×0.1`.

The cause: it clamped in both directions in a single pass and never revisited a
pinned name, so a name pinned to the cap could leave less mass than the
remaining floors required. It is reformulated as floor-plus-excess — seat every
name at the floor, then water-fill the remainder capped at `cap - floor` — and
verified across 4000 randomised feasible cases with zero invariant violations.

In production this would have misallocated capital. It is in
[`src/weights.ts`](https://github.com/fiduciaindex/fiducia/blob/main/packages/mandate/src/weights.ts),
and the comment now points at the tests instead of claiming them.

## Test coverage

`node --experimental-strip-types --test test/*.test.ts` → **46 tests, 46 pass, 0 fail**

| File | Covers |
| --- | --- |
| `canonical.test.ts` | Key ordering, whitespace, `undefined` vs `null`, non-finite and bigint rejection, address-case normalisation, hash stability under key reordering |
| `weights.test.ts` | Sum is exactly 10000 bps for every n in 2–50, largest-remainder rounding, cap and floor binding together, untradable names dropped and renormalised, infeasible bounds rejected |
| `planner.test.ts` | Leg sequencing, notional and slippage sizing, turnover and single-leg caps, guardrail blocking, `planHash` sensitivity to every field, cross-checked against an independent ABI restatement |

## Scope — what is and is not built

Stated plainly so the proof is not read as more than it is.

**Running and reproducible:** the deterministic core in `packages/mandate` —
mandate canonicalisation and hashing, the weight solver, the rebalance planner,
plan hashing, and the guardrail checks. 46 tests, 1134 lines including tests.

**Live:** the `$FIDUCIA` agent token on Virtuals (agent #139254) and the project
site at [fiduciaindex.com](https://fiduciaindex.com/).

**Written but not deployed:** the Solidity contracts (`IndexVault`,
`MandateRegistry`, `NavOracle`, `AttestationHub`). No Foundry tests yet, no
deployment, no audit.

**Not implemented:** the ACP adapter. No ACP job has been created or settled by
this project, which is why `primitives` lists only `token`. The agent processes
in `apps/agents` do not run without that adapter. No token utility is
implemented — all accounting in the planner is denominated in USDG, not
`$FIDUCIA`.

The repo README carries the same per-component status table.
50 changes: 50 additions & 0 deletions showcase/fiducia-deterministic-rebalance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Fiducia — deterministic rebalance planner

Reviewer notes for this showcase package.

## What to look at first

[`PROOF.md`](PROOF.md) is the proof document: the commands, the recorded demo
output, and the scope statement. Everything in it is reproducible from a clean
clone in under a minute, with no keys, no RPC endpoint, and no funded wallet.

```bash
git clone https://github.com/fiduciaindex/fiducia.git
cd fiducia/packages/mandate
pnpm install --ignore-workspace
node --experimental-strip-types --test test/*.test.ts # 46 pass
node --experimental-strip-types examples/deterministic-plan.ts
```

The demo prints `mandateHash 0xe3420dcc…5125dd` and
`planHash 0x07061e2f…3e30e`. Those exact hashes should appear on any machine; if
they do not, the determinism claim is false and this submission should be
rejected.

## Scope, stated up front

- **Runs:** the deterministic core — canonicalisation, hashing, the weight
solver, the rebalance planner, plan hashing, guardrails. 46 tests.
- **Live:** the `$FIDUCIA` agent token on Virtuals (#139254) and
[fiduciaindex.com](https://fiduciaindex.com/).
- **Not deployed:** the Solidity contracts. Written, no Foundry tests, no
deployment, no audit.
- **Not implemented:** the ACP adapter. No ACP job has been created or settled,
so `primitives` lists `token` only and no ACP claim is made anywhere in the
manifest.

The repo README carries the same table, marking each component as running or
not.

## Redaction

No credentials, keys, wallet material, or private instructions are in this
package. The only addresses are public: the USDG base token, the `$FIDUCIA`
token contract, and placeholder router addresses used by the demo fixture.

## Assets

`assets/poster.png` — 1600x900, generated by
[`scripts/showcase-poster.mjs`](https://github.com/fiduciaindex/fiducia/blob/main/landing/scripts/showcase-poster.mjs)
in the project repo. The figures on the card are copied from a real demo run,
not mocked up.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions showcase/fiducia-deterministic-rebalance/showcase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"slug": "fiducia-deterministic-rebalance",
"title": "Fiducia — Deterministic Index Rebalance Planner",
"tagline": "Turns an index mandate into a hash-committed rebalance plan, so the trades an agent is allowed to execute are fixed before it touches a router",
"description": "Fiducia plans rebalances for tokenised-equity index vaults. A mandate is canonicalised to bytes and hashed, target weights are solved in integer basis points, and the resulting leg sequence is hashed into a single planHash that an executor can be checked against — an agent cannot widen slippage, resize a leg, or swap the calldata without changing the hash. The core is pure computation with no RPC or keys, so anyone can clone the repo and reproduce the same mandateHash and planHash byte-for-byte. Writing the test suite found a real bug: the weight solver returned allocations summing to 130% when a cap and a floor bound at once, which is fixed and now covered by 46 passing tests plus a 4000-case randomised sweep.",
"status": "deterministic core running, contracts not deployed",
"topic": "agents",
"topics": ["agents", "security", "robinhood-chain", "tokenized-stocks", "rwa", "determinism", "risk"],
"builder": {
"name": "Fiducia",
"url": "https://x.com/fiduciaindex"
},
"links": {
"repo": "https://github.com/fiduciaindex/fiducia",
"demo": "https://fiduciaindex.com/",
"share": "https://x.com/fiduciaindex/status/2096257736510160916",
"feedback": "https://github.com/fiduciaindex/fiducia/issues/new?title=Showcase%20feedback%3A%20deterministic%20rebalance%20planner&body=Which%20prompt%20fits%3F%0A%0A-%20Should%20planHash%20cover%20execution%20deadline%20and%20gas%20ceiling%3F%0A-%20Is%20largest-remainder%20the%20right%20rounding%20rule%20for%20weights%3F%0A-%20Should%20a%20stale%20NAV%20oracle%20block%20or%20only%20warn%3F%0A%0ANotes%3A%0A"
},
"primitives": ["token"],
"visual": {
"kind": "reproducible cli output",
"eyebrow": "deterministic core",
"title": "mandate hash to plan hash",
"posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/fiducia-deterministic-rebalance/assets/poster.png"
},
"skills": [],
"artifacts": [
{
"label": "Reproducible demo — mandateHash, weights, plan, planHash, tamper check",
"href": "https://github.com/fiduciaindex/fiducia/blob/main/packages/mandate/examples/deterministic-plan.ts",
"kind": "proof"
},
{
"label": "Recorded output of that demo (planHash 0x07061e2f…3e30e, 100 rebuilds identical)",
"href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/fiducia-deterministic-rebalance/PROOF.md",
"kind": "proof"
},
{
"label": "Test suite — 46 tests over canonicalisation, weights, and plan hashing",
"href": "https://github.com/fiduciaindex/fiducia/tree/main/packages/mandate/test",
"kind": "proof"
},
{
"label": "The 130% weight bug and its fix",
"href": "https://github.com/fiduciaindex/fiducia/blob/main/packages/mandate/src/weights.ts",
"kind": "proof"
},
{
"label": "Public repo, with a README stating which components run and which do not",
"href": "https://github.com/fiduciaindex/fiducia",
"kind": "proof"
},
{
"label": "$FIDUCIA agent token on Virtuals",
"href": "https://app.virtuals.io/virtuals/139254",
"kind": "proof"
},
{
"label": "Project site",
"href": "https://fiduciaindex.com/",
"kind": "proof"
}
],
"feedbackPrompts": [
"Should planHash also commit to an execution deadline and a gas ceiling, not just the legs?",
"Is largest-remainder the right rounding rule when weights must sum to exactly 10000 bps?",
"Should a stale NAV oracle block a rebalance outright, or only warn and let it proceed?"
]
}
Loading