# Shared holder accounting and funded proofs

Musebook's `shared-core-warmup-v2` workflow actually calls the supplied Rust `fairlaunch-core` holder accounting. The host replay produces exact weights from ordered owner balances. The Python bridge then reuses the canonical funded rewards program's Merkle leaf and node functions. Neither step reads a wallet, calls RPC, signs, publishes a root, or funds an epoch.

The product's warm-up example is this explicit v2 policy. The existing `programs/musebook-holder-rewards/scripts/index_rewards.py` remains the earlier lot policy (v1); previously funded roots remain immutable. Selecting a different indexer for a new epoch cannot change the liabilities of an old epoch.

## The executable policy

| Rule | Shared core v2 |
| --- | --- |
| Warm-up | New balance waits 86,400 seconds. A top-up restarts the warm-up of all still-pending balance; existing eligible balance continues earning. |
| Loyalty | 1× to 2× linearly over 90 days. Age starts at the first purchase, including the warm-up day. |
| Accrual | Integrates the multiplier over the full interval. Integer points and a carried fractional remainder make checkpoint frequency irrelevant. |
| Reduction | Removes pending balance first. An eligible reduction forfeits the same fraction of the exact retained points and resets the remaining eligible balance's loyalty age to that timestamp. |
| Empty position | A later increase starts a new purchase age. |
| Windows | A window boundary clears earned points and their remainder while preserving the existing balance, pending maturity and purchase age. |
| Invalid input | Rejects clock rewinds, negative timestamps, invalid parameters, zero changes, overflow and tracked balance overdrafts. Every failing mutation leaves the holder state unchanged. |

The loyalty primitive uses a denominator `D = 2 × 10,000 × 7,776,000 = 155,520,000,000`. For age `a ≤ R` and extra multiplier `E = max_mult_bps − 10,000`, the numerator primitive is `20,000 × R × a + E × a²`. Beyond the ramp it is `2 × max_mult_bps × R × a − E × R²`. The replay computes each interval's primitive difference times the eligible balance, adding the previous remainder before division. U256 intermediate arithmetic bounds the multiplication; each holder's whole points are bounded to u128 and remainder to `0, D)`.

For example, 100 raw token units bought at timestamp 0 have zero points at the 24-hour boundary. Held through timestamp 172,800, they have exactly 8,784,000 points: only the second day earns, with the loyalty age spanning days one to two. Additional no-op balance checkpoints produce exactly the same points and remainder.

## Why v1 is a separate policy

| Behavior | Earlier lot indexer v1 | Shared core v2 |
| --- | --- | --- |
| New balance | Starts accruing immediately, with its own lot age. | Enters the 24-hour pending balance. |
| Top-up | Preserves old lot ages and adds a separate new lot. | Preserves eligible age; re-warms all pending balance. |
| Eligible sale or outgoing transfer | Forfeits points proportionally, preserving retained lot ages. | Forfeits points proportionally and resets retained eligible age. |
| Point arithmetic | Exact scaled per-lot integral. | Exact scaled holder integral with a carried remainder. |
| Merkle encoding | Canonical funded rewards v1 domains. | The same canonical funded rewards v1 domains. |

The default existing v1 script is preserved as supplied evidence. New v2 product artifacts use `parallel/fairlaunch/tools/index_shared_rewards.py` and explicitly include `accounting_policy: shared-core-warmup-v2`. Accounting policy is an off-chain allocation rule; an immutable funded root and the epoch's actual authorized signer remain the on-chain authority boundary. The on-chain program does not independently prove arbitrary wallet holding history or the policy label.

## Reproduce the integration

Run from the repository root:

```sh
cargo test --locked --manifest-path parallel/fairlaunch/core/Cargo.toml -p fairlaunch-core --all-targets
python3 -m unittest discover -s parallel/fairlaunch/tools -p 'test_index_shared_rewards.py' -v
cargo run --quiet --locked --manifest-path parallel/fairlaunch/core/Cargo.toml -p fairlaunch-core --example replay-holder-events -- parallel/fairlaunch/fixtures/holder-events.shared-v2.json
python3 parallel/fairlaunch/tools/index_shared_rewards.py parallel/fairlaunch/fixtures/holder-events.shared-v2.json
```

The bridge invokes the Rust example itself with `cargo run --locked`; its tests also execute this path. This is supplied-core reuse, with no independent Python copy of the v2 points formula. The current integration suite covers checkpoint invariance including fractional points, warm-up boundaries, pending top-ups, sale age resets, missing history, mixed holding mints, policy mismatch, input-hash binding, exact budget conservation, epoch binding and canonical proof verification. Core tests also cover failed-mutation rollback, maturity overflow, point overflow and extreme allocation products.

The two-holder fixture gives equal weights and an immutable total of 101 payout units. Largest remainder allocation assigns 51 and 50 units, breaking equal remainders by the owner's decoded public key. The generated artifact says `publication_status: unpublished` and records the input SHA256, exact parameters, source fingerprints, event provenance, positions, leaves and proofs. The holding mint and payout mint are distinct named identities. Merkle leaves bind the rewards program, epoch, payout mint, recipient and u64 allocation; changing the epoch changes the root even when weights stay the same.

The operator must independently review complete public history, create the intended epoch, publish through its actual authorized signer and fund the exact immutable total before claims become executable. This tool offers no publication command. Caller-provided `rpc-export` transaction signatures are checked for valid width and retained as provenance; neither signatures nor source URIs are independently verified here. Events must aggregate all of an owner's accounts for the holding mint, start with a known zero balance, retain consistent before/after balances, and have strictly increasing slot/transaction/event order and non-regressing timestamps. This does not replace an authenticated history exporter.

## Source map

- Shared accounting: [`parallel/fairlaunch/core/src/rewards.rs` (`parallel/fairlaunch/core/src/rewards.rs`).
- Host replay: `parallel/fairlaunch/core/examples/replay-holder-events.rs` (`parallel/fairlaunch/core/examples/replay-holder-events.rs`). The JSON, hashing and base58 dependencies are dev dependencies, outside the SBF consumer's normal dependency graph.
- V2 bridge and tests: `parallel/fairlaunch/tools/index_shared_rewards.py` (`parallel/fairlaunch/tools/index_shared_rewards.py`), `test_index_shared_rewards.py` (`parallel/fairlaunch/tools/test_index_shared_rewards.py`).
- Earlier policy and reused canonical Merkle encoding: `programs/musebook-holder-rewards/scripts/index_rewards.py` (`programs/musebook-holder-rewards/scripts/index_rewards.py`).
- On-chain Merkle contract: `programs/musebook-holder-rewards/src/merkle.rs` (`programs/musebook-holder-rewards/src/merkle.rs`).
- Canonical funded epoch authority and checks: `programs/musebook-holder-rewards/src/lib.rs` (`programs/musebook-holder-rewards/src/lib.rs`).
