diff --git a/showcase/fiducia-deterministic-rebalance/PROOF.md b/showcase/fiducia-deterministic-rebalance/PROOF.md new file mode 100644 index 0000000..f251330 --- /dev/null +++ b/showcase/fiducia-deterministic-rebalance/PROOF.md @@ -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. diff --git a/showcase/fiducia-deterministic-rebalance/README.md b/showcase/fiducia-deterministic-rebalance/README.md new file mode 100644 index 0000000..471fa33 --- /dev/null +++ b/showcase/fiducia-deterministic-rebalance/README.md @@ -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. diff --git a/showcase/fiducia-deterministic-rebalance/assets/poster.png b/showcase/fiducia-deterministic-rebalance/assets/poster.png new file mode 100644 index 0000000..6a04c9c Binary files /dev/null and b/showcase/fiducia-deterministic-rebalance/assets/poster.png differ diff --git a/showcase/fiducia-deterministic-rebalance/showcase.json b/showcase/fiducia-deterministic-rebalance/showcase.json new file mode 100644 index 0000000..628a030 --- /dev/null +++ b/showcase/fiducia-deterministic-rebalance/showcase.json @@ -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?" + ] +}