# Abundantia: Distribution, Payout, the Economy Firewall & Anti-Fraud

Mawu is a creator republic, so the question that decides whether it survives is
not "can a creator build a realm?" but "does a creator get paid correctly, and
is the money they earn protected from the people trying to steal or launder it?"
Abundantia is the answer: the platform-owned plane that distributes
content-addressed realm and mod artifacts over a CDN, computes every creator's
payout to the cent across three revenue rails, and then settles those payouts
through the shared **Aje** payment substrate. Around that plane sit two
non-negotiable defenses. The **economy firewall** keeps the real-money platform
economy one-directional — money flows _in_ to entitlements, and in-realm
play-currency can never cash out to real money through a community operator. The
**anti-fraud** layer makes the payout pool expensive to farm: only accounts that
have actually spent real money count toward it, retention is weighted over raw
concurrency, and a graph over account/realm/device/payment relationships flags
the gold-farming and self-dealing topologies that every creator-payout program
attracts.

This page is the engineering companion to the "Abundantia — Distribution and
Payout Architecture" and "The Economy Firewall and Anti-Fraud" summaries in the
orientation hub [../V7_ARCHITECTURE.md](../V7_ARCHITECTURE.md). Because
Abundantia is where real money meets community-hosted realms, it sits exactly on
the trust boundary: the catalog, the payout formula, and both firewalls are
platform-controlled and never delegated to a realm process, while the realm
keeps only its own non-fungible play economy. The settlement leg deliberately
stops at a typed contract and hands execution to the shared Aje plane — the same
seam V3's commerce service uses — so this document is careful to say which side
of that seam each piece of code lives on.

## What ships, honestly

The Abundantia market service is **real, tested TypeScript**:
`apps/v7/abundantia-market-service/src/service.ts` is a ~4.5 K-line module whose
service descriptor (port `47301`, `service.ts:1030`) advertises ~50
capabilities, and its `service.test.ts` suite runs **21 tests green**
(`vitest run` → `Tests 21 passed`, verified on box). The tests are not
truthiness checks — they assert exact computed amounts: a 100-cent engagement
pool splits to `33`/`17`/`16`/`34` across three creators and a declared
dependency; a direct sale of `10_000` gross with `800` tax and `1_200` refund
yields `13_000` net, `3_400` platform revenue, and a `4_200`-cent creator route;
a `6_999`-bps direct split (one basis point under the 70% floor) is rejected
outright. The payout formula, the verified-spend eligibility weighting, the
currency firewall's graph-reachability check, the fraud-graph topology detector,
the commerce-ledger isolation, and the content-addressed catalog are all
domain-specific and exercised against known-correct values.

**Three honest qualifications.** _First_, like V3's commerce crate, Abundantia's
job is the **ledger**, not the wire. `evaluateAbundantiaPayoutFormula`
(`service.ts:1741`) computes splits, holds, and compliance gates and emits a
typed `AbundantiaAjeSettlementReceipt` carrying deterministic
`aje:v7:abundantia:<hash>` transfer IDs (`createAjeTransfer`, `service.ts:3589`)
— it does **not** import `@oshun/payments-bridge` or broadcast a transaction.
The real Aje plane (Ed25519 receipt signing, the cross-rail entitlement bus, the
cold-spend co-signing queue) lives in `libs/oshun/payments-bridge` and is the
substrate that actually executes settlement; Abundantia produces the contract it
settles. _Second_, the **Nàná economy crate** (`libs/v7/nana/src/lib.rs`) is
deliberately minimal today — it defines `NanaCharacterRecord`, `NanaBalances`
(cash/bank/society in minor units), and `validate()` (which rejects negative
balances), and its own doc-comment says it "intentionally starts small." The
realm-scoped double-entry ledger, faucet/sink primitives, and auto-balancer
described in the hub are **architecture, not yet code in that crate**. _Third_,
the **Year-1 catalog** — which realms, which Collections, which featured drops —
is a schedule the service executes, not an artifact in the repo. The machinery
ships; the merchandise is operations.

## Distribution — catalog, CDN, and one-click install

Every Abundantia artifact is **content-addressed**, which is the single
primitive the whole distribution layer is built on.
`computeAbundantiaContentHash` (`service.ts:1136`) hashes the schema tag, the
content bytes, and the **sorted dependency-closure hashes** into a `sha256:` id,
so an artifact's identity changes the instant any byte of it or any of its
dependencies changes. `buildContentAddressedCdnObject` (`service.ts:1153`) turns
that hash into a CDN URL with an integrity field equal to the hash itself, so a
swapped dependency is tamper-evident at fetch time. This is the same Nix-model
content addressing the Ixchel resolver uses for its lock file — distribution and
dependency resolution agree on one id.

`publishCrossPlatformCatalogArtifact` (`service.ts:1174`) is the publish gate.
It requires a `sha256:`-addressed content hash, an attested rights claim, and a
Sekhmet review of status `approved` before a record is produced, and it computes
**per-platform availability** separately: PC publishes immediately for a trusted
creator, while console targets (xbox/playstation/switch) stay
`requires_console_approval` until a matching `AbundantiaConsoleAuthorization`
with a passed cert checklist is supplied. A single account's installs then sync
across platforms (`installCatalogRecordForAccount`, `service.ts:1243`) — the
mod.io "one account, every device" model — and the in-game browser
(`queryInGameCatalogBrowser`, `service.ts:1370`) refuses to surface anything not
Sekhmet-cleared and **visibly flags degraded realms** rather than presenting
them as healthy, consuming the gateway's per-realm health signals.

The headline distribution flow is **atomic Collection install**.
`installCollectionAtomically` (`service.ts:1574`) resolves a root listing's full
dependency closure, verifies every artifact in the closure is Sekhmet-cleared,
computes a lock-file hash, and only then fetches — and the `service.test.ts`
"aborts Collection install atomically before fetching an uncleared dependency"
case proves the all-or-nothing property: one uncleared dependency anywhere in
the closure fails the whole install with **zero artifacts fetched**, so a player
never ends up with a half-installed Collection straddling a quarantined mod.

## Payout — three rails, weighted to the cent

Abundantia pays creators across **three rails**, all reconciled by one formula:
the **engagement pool** (a platform-funded pot, default 40% of eligible net
revenue, distributed by weighted engagement), **direct sale/subscription** (with
a hard ≥70% creator floor), and **dependency-revenue chains** (a configured
share routed to the creators of an artifact's declared dependencies). The pool
size and the dependency-share percentage are governance-set inputs to the
formula (`engagementPoolCents`, `dependencyShareBasisPoints`), amendable via
Eunomia; the 70% floor is the one rate hard-coded as an invariant.

### Who counts: verified-spend, retention-weighted eligibility

Before a single cent enters the engagement pool, `runPayoutEligibilityEval`
(`service.ts:1951`) decides which play counts. It is the anti-bot lever made
concrete: a session is **excluded** if the account has never spent real money
(`verified_spend_required`) or is a flagged CCU-inflation bot
(`ccu_inflation_bot`). Surviving sessions are weighted by
`buildPayoutEligibilityWeightedSession` (`service.ts:3022`):
`engagementMinutes × (retentionMultiplier + cohortBonus) / 10_000`, where the
retention multiplier adds 1_000 bps per retained active day (capped at 7 days)
and new/reactivated payers get a 2_500-bps bonus. The test pins the math: a
40-minute session from a 4-day-retained new payer weights to exactly
`floor(40 × (14_000 + 2_500) / 10_000) = 66`, while a 200-minute never-spent
session and a 999-minute bot session both weight to **zero** — raw concurrency
buys nothing.

### The split formula

`evaluateAbundantiaPayoutFormula` (`service.ts:1741`) is the heart of the page.
For each direct-revenue line it computes net = `gross − tax − refund`, takes the
platform cut, and carves the creator pool at `creatorShareBasisPoints` —
rejecting any line below 7_000 (`validatePayoutFormulaInput`,
`service.ts:3311`). It then allocates the engagement pool across eligible
signals by weight (`allocateCentsByWeight`, a largest-remainder method so the
cents always sum exactly), and runs `splitCreatorAndDependencyRevenue`
(`service.ts:3378`) to carve each primary creator's amount into the creator's
own route plus pro-rata **dependency-revenue** routes to declared dependencies.
The worked test case exercises all of it at once: Ada's `10_000`-cent sale (net
`8_000`, 70% share, 25% dependency carve) yields a `4_200` creator route and a
`1_400` dependency route to Cy; Bea's subscription, still inside its refund
window, is computed but fully **held** (`holdReason: refund_window_open`).
Totals reconcile to `creatorEarningsCents: 9_700`,
`platformRevenueCents: 3_400`, `heldCents: 4_551`.

### Compliance gates and the held-vs-cashable distinction

A computed route is not a cashable payout. `buildCreatorPayoutSettlements`
(`service.ts:3448`) walks each creator's routes through `complianceGateReasons`
(`service.ts:3560`): missing KYC, an incomplete tax interview, or an unverified
payout method each **hold the entire payable amount**, as do a reserve hold and
an unmet minimum-payout threshold. In the worked case Ada clears (cashout
`3_733` after a `500` reserve hold), Cy clears (`1_416`), but Bea is gated
`payout_method_required` and Dia is gated `kyc_required, tax_interview_required`
— both cash out **zero**. The settlement status reflects this honestly:
`payoutSettlementStatus` (`service.ts:3662`) returns `partially_held` whenever
anything is held, `rejected` when validation fails (the sub-floor case produces
`rejected` with no transfers at all), and `settled` only when every cent is
clear.

### The dependency lock-file path and the Aje seam

`computeDependencyRevenueSharesFromLockFile` (`service.ts:1805`) derives the
dependency set straight from a realm's resolved lock file — the same
content-addressed closure the Ixchel resolver pins — and allocates the
configured dependency share across it by weight, so the people whose mods a
realm depends on are paid **by construction from the dependency graph**, not by
manual declaration. Every clear settlement becomes an `AbundantiaAjeTransfer`
with a deterministic `aje:v7:abundantia:<24-hex>` id and a per-rail breakdown,
wrapped in an `AbundantiaAjeSettlementReceipt` carrying a SHA-256 ledger hash.
That receipt is the typed contract handed to the shared Aje plane —
`@oshun/payments-bridge` — whose entitlement bus normalizes four fiat processors
and fourteen crypto rails into one `payment.invoice.settled` envelope
(`entitlement-bus/topics.ts`) and whose receipt-signer signs each payout with
Ed25519. Abundantia decides _who gets how much_; the bridge decides _how the
money moves_.

## The economy firewall

```mermaid
flowchart TD
    subgraph Realm[REALM PLANE — non-fungible play economy]
      PLAY[realm_play_currency / inventory]
      OPW[realm_operator_wallet]
    end
    subgraph Plat[PLATFORM PLANE — real money, one-directional]
      BUY[Purchase: Stripe / Adyen / PayPal / 14 crypto rails]
      ENT[platform_entitlement]
      POOL[Engagement pool · 40% net]
      DIRECT[Direct sale / subscription · ≥70% floor]
    end
    BUY -->|real_money_purchase| ENT
    ENT -->|realm_currency_grant| PLAY
    PLAY -. operator_redemption_request .-> OPW
    OPW -. cash-out .-x PAYOUT
    ELIG[runPayoutEligibilityEval<br/>verified-spend + retention] --> POOL
    FRAUD[runFraudGraphEval<br/>self-dealing + RMT topology] -. blocks .-> ELIG
    POOL --> FORMULA
    DIRECT --> FORMULA
    FORMULA[evaluateAbundantiaPayoutFormula<br/>split · dependency carve · gates] --> RCPT
    RCPT[AjeSettlementReceipt<br/>aje:v7:abundantia:… transfers] -->|typed seam| AJE[shared @oshun/payments-bridge<br/>Ed25519 · entitlement bus]
    AJE --> PAYOUT[Creator payout account]
```

The firewall has two halves, both enforced in code.

### The currency firewall — realm value cannot reach real money

`runCurrencyFirewallEval` (`service.ts:1843`) classifies every economic node as
**realm-value** (`realm_play_currency`, `realm_inventory_item`,
`realm_operator_wallet`) or **real-money** (`platform_real_money_account`,
`platform_entitlement`, `creator_payout_account`) and proves no value can flow
from the first class to the second. It does this two ways. First, a per-transfer
guard (`blockedCurrencyTransfer`, `service.ts:3967`) blocks any realm-value →
real-money edge and any `operator_redemption_request` originating from realm
value. Second — and this is the part a naive check misses —
`findCurrencyFirewallLeaks` (`service.ts:4005`) runs a **depth-first
reachability search** from every realm-value node over the surviving
(non-blocked) transfers, and fails the eval if any path reaches a real-money
node, catching laundering that hops through intermediaries. The test proves the
intent: an operator may sweep play-currency within their own realm (allowed),
but the moment they try to `operator_redemption_request` from the operator
wallet to an Aje payout account, that transfer is **blocked**, no leaking path
survives, and the eval is clean. This is what keeps casinos and loot mechanics
out of gambling regulation — there is no real-money cash-out, by construction.

### Commerce-ledger isolation and the no-paywall-on-free rule

`runRealMoneyCommerceSurfaceEval` (`service.ts:1872`) keeps the platform
real-money ledger and the realm play-currency ledger **physically separate**,
hashing each independently and refusing to mix entries, and it composes the
currency firewall so isolation and non-convertibility are checked together. It
also enforces a creator-protection rule the test names directly: a release that
shipped free **cannot later be paywalled** (`freeReleasePaywallRejected`), so a
creator can't bait-and-switch a community that already adopted their free
content.

## Anti-fraud

Payout fraud is the predictable consequence of paying for engagement, and
Abundantia attacks it at two layers. The **eligibility layer above** is the
first and strongest lever — farms can't cheaply fake verified real-money spend,
and weighting retention over concurrency kills CCU-inflation bots before they
reach the pool. The second layer is the **fraud graph**.

### The fraud graph — self-dealing and gold-farming topology

`runFraudGraphEval` (`service.ts:2000`) builds indexes over
account↔realm↔device↔payment edges, computes a degree-centrality score per
account, and runs two detectors. `findSelfDealingFraudGraphFlags`
(`service.ts:2275`) catches a creator farming their own realm: a realm whose
**owner** and a cluster of **never-spent players** share a device or payment
instrument is flagged `block`. `findRmtFraudGraphFlags` (`service.ts:2330`)
catches the gold-farming topology — a recipient account receiving
**one-directional "free money"** inflows from ≥3 source accounts, ≥2 of them
never-spent, exceeding a 10_000-unit threshold, with anomalous centrality
(degree ≥5). The thresholds are named constants (`service.ts:1024-1028`), and
the test drives a synthetic 15-node graph to exactly two flags: the self-deal
ring (creator + two never-spent alts on a shared console and card,
`neverSpentAccountCount: 2`) and the RMT broker (degree `6`, four never-spent
farm accounts wiring `4_000` units each for `16_000` total inflow over four
transfer edges). The detector reports the central account, the implicated
devices and payment instruments, and the specific transfer edges — actionable
evidence, not a bare score.

Adjacent to fraud, `runRightsProvenanceEval` (`service.ts:1912`) blocks the
re-upload of a known-infringing content hash, walks the asset attribution chain,
preserves fork-revenue links, and routes any DMCA claim to Themis — so a creator
who forks another's work carries the revenue link forward and a rights-holder's
takedown lands in the adjudication path rather than a support inbox.

### What's real vs. spec

The **graph-topology detectors above are real, tested code** — degree
centrality, one-directional-flow detection, never-spent and shared-device
clustering. What the hub additionally describes — an **ML classifier scoring
engagement _quality_** from retention-predictive actions (the random-forest /
graph-model literature) — is **spec, not yet implemented**: the current
eligibility weighting is the deterministic retention/cohort formula, and the
graph detectors are deterministic heuristics, not a trained model. The honest
boundary is that the structural anti-fraud (verified-spend gating, topology
flags) ships and is exercised; the learned engagement-quality model is future
work.

## Related

- [Nàná Data Model and Pheme Voice](./nana-data-model-and-pheme-voice.md) — the
  realm-scoped character and play-economy aggregate whose balances the currency
  firewall keeps non-fungible with real money
- [Eunomia Governance and the Ori Bridge](./eunomia-governance-and-ori-bridge.md)
  — the governance process that amends the 40% pool, dependency-share, and floor
  parameters, and the passport that carries creator earnings across realms
- [Oshun Domain Libraries](../../platform/oshun-domain-libraries.html) — where
  the shared Aje plane `@oshun/payments-bridge` (Ed25519 receipts, the
  cross-rail entitlement bus, cold-spend settlement) that executes Abundantia's
  payouts lives
- The orientation hub [../V7_ARCHITECTURE.md](../V7_ARCHITECTURE.md)
