Caution
NOT AUDITED - DO NOT USE REAL FUNDS YET
This repository is a working local/testnet prototype. Do not deploy, deposit, or route real capital to this treasury on mainnet until an independent security audit has been completed and published.
Warning
This system is a smart contract prototype for rebate accounting and custody. It does NOT solve physical supply chain problems:
- No Physical Medicine or Inventory Guarantees: This protocol records accounting entries and custody movements. It does not track, verify, or guarantee physical inventory, drug availability, delivery, pharmacy stock, clinical outcomes, or patient access. On-chain actions do not guarantee physical medicine supply or pharmacy fulfillment.
- Mutual Credit Vouchers = Ledger Reservations Only: Mutual credit vouchers create ledger capacity reservations only — they do not represent or guarantee physical medicine on-chain, and they do not bypass local supply shortages or distributor caps.
- Mutual Credit Clearing ≠ Guaranteed Supply: The mutual credit clearing mechanism operates as a liquid accounting framework for settlements and liquidity support between pharmacies. It does not guarantee physical supply, logistics, or distribution capacity.
- Scarcity Triage = Proposed Only: Scarcity-based triage policies and design proposals are theoretical frameworks. They are not executed autonomously on-chain or enforced by smart contracts.
- Centralized Governance Control: The
COUNCIL_ROLEretains significant administrative control. Autonomy is restricted to off-chain governance and multi-sig operations; there is no autonomous on-chain local federation execution. - Privacy and Linkage Risks: Legacy registration still uses stable credential hashes on-chain, creating a persistent identification vector that could be used for profiling or blacklisting. A mock ZK/nullifier registration slice now exists for semantic testing in
PatientFundParticipatoryBudgeting, but it is not production privacy:msg.sender, public voting events, transaction gas source, timing, and RPC metadata remain linkable. The production zk-nullifier design in IDENTITY_NULLIFIER_DESIGN.md remains a design specification only. - Participatory Budgeting Solvency Debt (Liveness & Trust Risk): The contract no longer hard-reverts when round start/finalization finds the patient-fund balance below outstanding obligations. It records the shortfall as
totalDebt/roundDeficitand lets the round lifecycle continue, but actual project claims still require real token liquidity. Council or donor top-ups remain necessary to clear debt and make underbacked shares claimable. - No Professional Advice: All outputs, files, and dashboards are for local prototype demonstration only. None of the contents constitute legal, financial, or medical advice.
For deeper documentation on safety, scarcity, and threat boundaries, see SCARCITY_GOVERNANCE.md, CARE_CONTINUITY.md, and RETALIATION_AND_PRIVACY_THREAT_MODEL.md.
The current checkpoint is a prototype with tested treasury, voting, mutual-credit, portability, and dashboard surfaces, but it is not audited, not mainnet deployed, and not public-wallet ready. Runtime behavior and policy boundaries are tracked in MECHANISM_COVERAGE.md; care-continuity and participant-safety boundaries are documented in CARE_CONTINUITY.md, RETALIATION_AND_PRIVACY_THREAT_MODEL.md, and SCARCITY_GOVERNANCE.md. Public launch remains blocked by PRODUCTION_READINESS_CHECKLIST.md.
| Surface | Status |
|---|---|
PBMRebateTreasury |
Working Solidity contract with epoch escrow, Merkle claims, dispute handling, sanctions, recall, pause, and cap controls |
PatientFundParticipatoryBudgeting |
Working patient-fund voting prototype with council registration and relayer-assisted voter self-registration |
| Mock ZK/nullifier registration | Semantic mock only: verifier/root/version-gated registration and nullifier reuse tests exist, but production unlinkability is not implemented |
PharmacyMutualCredit |
Working decoupled mutual-credit prototype with reserved-capacity, recipient-bound vouchers |
| Experimental governance/manifold drafts | Draft prototypes deployed only in focused tests for review; not integrated into treasury/PB/mutual-credit runtime and not production mechanisms |
| Tests | Compile and unit tests run in CI; run locally with npm.cmd test |
| Dashboard | Static prototype with local/test Web3 integration, synthetic-data labels, accessibility improvements, and offline verifier panel |
| Merkle tooling | Allocation root/proof generator |
| Portability export | Prototype JSON export plus offline self-consistency checks; RPC-backed verification is required for chain provenance |
| Continuity artifacts | Draft Node.js and static-browser tools for local paper-voucher and relay-intake review; not a live relay or on-chain settlement authority |
| Mainnet | Not deployed |
| External audit | Pending |
Prereqs: Node.js 20 LTS or 22 LTS + npm.
Windows PowerShell note: if npm is blocked by script execution policy, use npm.cmd instead.
npm.cmd ci
npm.cmd run compile
npm.cmd testGenerate Merkle roots and proofs:
npm.cmd run merkle:allocations -- --in allocations.json --out merkle.jsonRun the portability export prototype:
node scripts/export-portability.js \
--exporter <participant_address> \
--from-block <deployment_block> \
--to-block <end_block_or_latest>Exports fail closed by default on RPC/query failures or missing Merkle allocation material. --allow-partial and --allow-unbounded-query are explicit prototype/debug overrides, not production defaults.
Verify a portability export:
npm.cmd run verify:export -- --file exports/<participant_address>.jsonOffline verification requires proof material when claims are present, but it only checks untrusted export self-consistency and Merkle math. Use --allow-incomplete only when intentionally inspecting an incomplete export; add --rpc <url> for chain-provenance verification.
Draft continuity tooling is available at tools/resilience/continuity-engine.mjs,
tools/offline/continuity-kit.html, and tools/resilience/proxy-validator.js.
The Node engine requires a local LOCAL_MAC_SECRET, fails closed on missing or
invalid MACs, and produces review/intake artifacts only. The static browser kit
uses Web Crypto for local voucher MAC checks. The proxy validator checks relay
batch shape, duplicate nullifiers, and metadata leakage. None of these artifacts
is a live proxy relay, redemption path, settlement authority, or production ZK
proof generator.
flowchart LR
Depositor["PBM / depositor"] --> Treasury["PBMRebateTreasury"]
Council["Council Safe"] --> Treasury
Confirmer["Root confirmer"] --> Treasury
Guardian["Guardian pause key"] --> Treasury
Timelock["Timelock executor"] --> Treasury
Remediation["Exclusion remediation reserve"] --> Treasury
Treasury --> Pharmacy["Merkle pharmacy claims"]
Treasury --> PatientFund["Patient fund"]
PatientFund --> PB["PatientFundParticipatoryBudgeting"]
Voters["Credentialed voters"] --> PB
PB --> Projects["Community health projects"]
Issuer["Credential issuer / relayer"] --> PB
MutualCredit["PharmacyMutualCredit"] -. decoupled .-> Pharmacy
MutualCredit --> Vouchers["Recipient-bound vouchers"]
Dashboard["Dashboard prototype"] --> Treasury
Dashboard --> PB
Dashboard --> MutualCredit
Export["Portability export + verifier"] --> Treasury
Export --> PB
- Treasury remediation, root-backed claims, governance funds, and epoch escrow are separated in contract accounting.
- Voter registration supports EIP-712 relayer authorizations and direct trusted-issuer credential signatures.
- Patient-fund matching now uses pull-based project claims after round finalization.
- Mutual-credit vouchers reserve issuer capacity and can be redeemed only by the intended registered recipient.
- Dashboard values are explicitly synthetic unless contract-backed.
- Portability exports can be checked offline for structure/Merkle math and with RPC for chain provenance.
PBMRebateTreasury is an Ethereum smart contract that:
- records rebate deposits on-chain with depositor identity, amount, timestamp, and a free-form source string (which may encode quarter and drug class);
- routes captured funds to independent pharmacies through Merkle-proof claims;
- allocates 10% of every gross claim to a dedicated patient fund at claim time;
- makes ledger absences visible when paired with independently sourced expected-deposit records;
- separates treasury custody from adjacent prototypes such as mutual credit, vouchers, dashboard tooling, and participatory budgeting.
This is infrastructure for transparent rebate pass-through. It is not legal, financial, medical, or investment advice.
- PBM or depositor calls
depositRebate(). - The council Safe calls
proposeRoot()for the current epoch. - A separately configured root-confirmer Safe calls
confirmRoot(). - Pharmacies claim with Merkle proofs through
claim(). - Council calls
finalizeEpoch()to close the epoch. - After the 30-day
RECALL_DELAY, unclaimed funds can be recalled topatientFund.
| Bucket | Allocation | Purpose |
|---|---|---|
| Distribution pool | 99% | Pharmacy Merkle claims |
| Governance reserve | 1% | Council operations through EXECUTOR_ROLE |
| Source | Amount |
|---|---|
| Every gross claim | 10% routed to patientFund |
| Unclaimed epoch funds after recall delay | 100% routed to patientFund |
| Non-payout token sweeps | 100% routed to patientFund |
If patientFund is configured directly as the PatientFundParticipatoryBudgeting contract, payout-token inflows and direct payout-token surplus in that contract are treated as patient-bound matching liquidity for the next round and logged with ExternalPatientFundsApplied. They are not treated as council-refundable funds.
| Role | Holder | Permissions |
|---|---|---|
COUNCIL_ROLE |
3/5 Gnosis Safe | Epoch management, root proposal, recall, sanctions, unpause |
ROOT_CONFIRMER_ROLE |
Separate Safe or governance address | Independently confirms proposed Merkle roots; rotation is timelocked |
EXECUTOR_ROLE |
TimelockController | Cap changes, governance reserve withdrawal, environment fund update, confirmer rotation |
GUARDIAN_ROLE |
Separate fast-response address | Emergency pause only; cannot unpause or access funds |
- Hard cap enforced at root proposal and claim.
- Daily cap enforced at root proposal and claim.
- Root total enforced at claim.
- Per-pharmacy cap enforced through Merkle leaf encoding.
- Double-hash leaf construction for second-preimage protection.
- Root publication requires proposal by
COUNCIL_ROLEand approval by a separately configuredROOT_CONFIRMER_ROLE. - Council and root-confirmer membership are mutually exclusive, including future role rotations.
EXECUTOR_ROLEandROOT_CONFIRMER_ROLEadministration is controlled by the timelock rather than council default administration.- Daily cap remains bounded by hard cap.
- Recall only after
RECALL_DELAY, only for unclaimed amount, sent topatientFund. - Payout token cannot be swept.
- Non-payout tokens are swept to
patientFund, not a general fund. GUARDIAN_ROLEis separate fromCOUNCIL_ROLE.flagClaimrequires a valid Merkle proof.- Disputed active-epoch claims update cap and recall accounting consistently.
- Root-exclusion payouts require independent confirmer approval and remain bounded by epoch caps.
- Root-exclusion payouts use a separately funded remediation reserve and cannot consume root or future distribution liquidity.
- Root-backed claims, exclusion payouts, and escrow-backed unclaimed balances are reported separately.
- Dismissed exclusion claims cannot redirect unreserved treasury funds as a penalty.
- Sanctioned addresses cannot flag claims.
- Open dispute flag blocks a parallel claim on the same epoch.
- ETH is rejected through
receive()andfallback(). - No upgradeability.
// Double-hash leaf. abi.encodePacked is safe here because all fields are fixed-size.
bytes32 leaf = keccak256(
bytes.concat(keccak256(abi.encodePacked(pharmacy, grossAmount, eligibleCap)))
);Each leaf encodes:
pharmacy: claimant addressgrossAmount: gross allocation for this epocheligibleCap: per-pharmacy maximum enforced on-chain
Off-chain tooling must use
encodePacked, notencode, when hashing leaves.
constructor(
address _token,
address _patientFund,
address _environmentalFund,
uint256 _initialDailyCap,
uint256 _minimumEpochVolume,
address _council,
address _rootConfirmer,
address _executor,
address _guardian
)Before mainnet deployment:
- complete a formal security audit;
- configure a 3/5 Gnosis Safe for
_council; - configure a separate Safe or governance address for
_rootConfirmer; - deploy and configure a
TimelockControllerfor_executor; - confirm
_guardianis separate from council; - verify every address on the target network.
- set
_initialDailyCapand_minimumEpochVolumein the payout token's smallest units (for example, six-decimal units for USDC).
This repo includes a convenience Hardhat script:
scripts/deploy-timelock-and-treasury.js
Required environment variables:
TOKENPATIENT_FUNDENVIRONMENTAL_FUNDINITIAL_DAILY_CAPMINIMUM_EPOCH_VOLUMECOUNCILROOT_CONFIRMERGUARDIAN
Required timelock setup variable:
TIMELOCK_ADMIN- explicit temporary or retained admin; never defaults silently to council
Optional timelock variables:
TIMELOCK_MIN_DELAY_SECONDSTIMELOCK_PROPOSERSTIMELOCK_EXECUTORS- comma-separated executor addresses; setALLOW_OPEN_TIMELOCK_EXECUTOR=trueonly if open execution is intentionalALLOW_OPEN_TIMELOCK_EXECUTOR=true- explicit acknowledgement that ready timelock operations may be executed by any addressRENOUNCE_TIMELOCK_ADMIN=true- supported only whenTIMELOCK_ADMINis the deployer; removes the temporary human admin after deployment checks
Audit a deployed timelock and treasury against the expected environment configuration:
npm.cmd run audit:deployment -- --network <network>The audit requires the deployment variables above plus TIMELOCK_ADDRESS and
TREASURY_ADDRESS. It verifies the deployed constructor bindings, caps, role
memberships, role administrators, timelock delay, proposers, executors, and
external-admin state. After legitimate cap ratchets, set
EXPECTED_DAILY_VOLUME_CAP and EXPECTED_HARD_ABSOLUTE_VOLUME_CAP to the
expected current values; otherwise the original deployment cap values are used.
On non-local networks the audit is strict and also requires COUNCIL_SAFE_OWNERS,
COUNCIL_SAFE_PROXY_CODE_HASH, and COUNCIL_SAFE_SINGLETON; if any Safe modules
are intentionally enabled, set COUNCIL_SAFE_MODULES and matching
COUNCIL_SAFE_MODULE_CODE_HASHES.
| Item | Status |
|---|---|
| Internal review | Complete enough for prototype iteration |
| External audit | Pending |
| Mainnet deployment | Not deployed |
| Production frontend build | Build/check scripts present; public deployment still pending |
| Database/API/RLS surface | Not present yet |
| Rate limiting, caching, scaling, observability | Design gate only; not implemented yet |
| ADA/WCAG production audit | Pending |
| Production readiness checklist | See PRODUCTION_READINESS_CHECKLIST.md |
| Mechanism coverage | See MECHANISM_COVERAGE.md |
| Security reporting | See SECURITY.md |
| Open product decisions | See OPEN_DESIGN_DECISIONS.md |
| Implemented design decisions | See DESIGN_DECISIONS.md |
To preserve treasury simplicity, these systems are intentionally decoupled:
PatientFundParticipatoryBudgeting: patient-fund project allocation prototype.PharmacyMutualCredit: mutual-credit and recipient-bound emergency voucher prototype.tools/credentials: credential issuance and verification prototype with wallet binding, expiry, and local revocation checks.scripts/export-portability.js: portability export prototype.dashboard/: static dashboard and local/test Web3 prototype; omission examples use provenance-labeled synthetic organizations rather than claims about real PBMs.
The project is motivated by rebate pass-through gaps affecting independent pharmacies and patient access. Policy references in this repository are context for the model, not legal conclusions. Any procurement clause, ERISA-facing language, deployment plan, or real-funds workflow requires qualified legal review.
Contributions are welcome, especially:
- test suite expansion;
- dashboard hardening and accessibility;
- Merkle and portability tooling;
- documentation cleanup;
- security review.
Open an issue before submitting a large PR.
MIT. See LICENSE.
Independent pharmacies serve communities that large chains abandon. They dispense prescriptions on thin margins, absorb clawbacks they cannot audit, and often lack a durable ledger to point to when the numbers do not add up.
This repository explores that ledger.
Every deposit is permanent. Every ledger absence is visible; calling it an omission requires independent expected-deposit evidence. The machine comes first; the mission can stand on it.