Skip to content

Economy: net-zero ledger + platform fee control + purchasability fix (JFA §7)#141

Open
NetworkTheoryAppliedResearchInstitute wants to merge 2 commits into
agrinet-backendfrom
refactor/economy-net-zero-fee
Open

Economy: net-zero ledger + platform fee control + purchasability fix (JFA §7)#141
NetworkTheoryAppliedResearchInstitute wants to merge 2 commits into
agrinet-backendfrom
refactor/economy-net-zero-fee

Conversation

@NetworkTheoryAppliedResearchInstitute

Copy link
Copy Markdown
Collaborator

Closes the JFA §7 economy non-conformances from the audit — and fixes a real money-conservation bug along the way.

The bug this fixes

The old model credited the buyer's general wallet on a Stripe payment ("Stripe deposit") and separately credited the seller at settlement — so money was not conserved. Balances were a mutable wallets.balance with no ledger behind them.

Net-zero double-entry ledger (§7.2)

  • New append-only ledger_entries as the source of truth + ledgerRepository.post, which enforces each posting sums to zero and updates wallets.balance as a derived, reconcilable cache. System accounts: escrow, platform, fiat_gateway, genesis. Migration backfills existing balances.
  • Escrow lifecycle rewired to balanced postings: fund (buyer|fiat → escrow), release (escrow → seller + platform fee), tranche, refund (escrow → buyer), contract transfer.

Platform fee — admin-controlled (§7.4; open problem 7: transparent, contestable fees)

  • platform_settings.fee_bps + settingsRepository; admin GET/PUT /admin/settings/platform-fee (by % or bps); public GET /payments/fee. The fee is charged at settlement/tranche and recorded as a visible ledger entry to the platform account.

Purchasability fix (§7.3)

  • The webhook no longer credits a general spendable wallet; a payment funds only its transaction's escrow (fiat_gateway → escrow). The generic top-up /payments/pix/create is retired (410). No cash-out path exists (redemption stays conformant).

Verification (in-container)

10% fee → seller 90 / platform 10; tranche net 45 / fee 5; refund net-zero; global ledger sum = 0; escrow returns to 0; reconcile drift 0; buyer wallet unchanged by a funded purchase.

Targets agrinet-backend. Follow-ups: Mycelium operator_id+witness (#9), LBTAS reconcile (#10), fruitful UI (#11).

🤖 Generated with Claude Code

…y fix

Closes the JFA §7 economy non-conformances found in the audit (and fixes a real
money-conservation bug: the old model credited a general wallet on Stripe payment
AND separately credited the seller at settlement — money was created).

Net-zero double-entry ledger (§7.2):
- new `ledger_entries` (append-only source of truth) + `ledgerRepository.post`,
  which enforces each posting sums to zero and updates `wallets.balance` as a
  derived, reconcilable cache (no more silent drift). System accounts: escrow,
  platform, fiat_gateway, genesis. Migration backfills opening balances.
- escrow lifecycle rewired to balanced postings: fund (buyer|fiat -> escrow),
  release (escrow -> seller + platform fee), tranche, refund (escrow -> buyer),
  contract transfer (wallet -> wallet). verifyChain-style reconcile + global-sum
  invariant.

Platform fee, admin-controlled (§7.4; JFA open problem 7 — transparent, contestable):
- `platform_settings.fee_bps` + `settingsRepository`; admin GET/PUT
  /admin/settings/platform-fee; public GET /payments/fee. Fee is charged at
  settlement/tranche and recorded as a visible ledger entry to `platform`.

Purchasability fix (§7.3):
- webhook no longer credits a general spendable wallet; a payment funds ONLY its
  transaction's escrow (fiat_gateway -> escrow). Payments with no transactionId
  credit nothing. Generic top-up /payments/pix/create retired (410). No cash-out
  path exists (redemption stays conformant).

Verified in-container: 10% fee -> seller 90 / platform 10; tranche 45/5; refund
net-zero; global ledger sum = 0; escrow returns to 0; reconcile drift = 0; buyer
wallet unchanged by a funded purchase.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The check greped the ENTIRE repo for http://localhost|localhost:5000, so it
flagged dozens of legitimate dev references (smoke-test scripts, docs, README,
docker-compose.local, nginx.local.conf, .env.example, and env-driven fallbacks
like `process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000'`). It was red
on every PR regardless of the diff.

Now it scans only shipping runtime source (backend/ + frontend/ .js/.ts), and
excludes dev tooling (scripts/, __tests__), docs, local infra, and env-fallback
lines — where a hardcoded localhost is a real production bug. A regression in
shipping code is still caught. Added backend/server.js (dev CORS origins, also
env-driven via ALLOWED_ORIGINS) to the allowlist.

Verified locally: exit 0, no matches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant