Soroban smart contracts for Lafiya's on-chain trust layer β an attestation registry and attester allowlist that let a health worker's verification of an emergency health record be checked cryptographically, without the underlying health data ever touching the blockchain.
Your vitals, verified. When you can't speak, Lafiya does.
Lafiya is Hausa for health, safety, and wellbeing.
Status: Pre-alpha Β· Stellar testnet Β· not yet audited Β· not a medical device. See Disclaimer.
π Documentation: https://Lafiya-xyz.github.io/Lafiya-contract/
Lafiya is a free, patient-owned emergency health card: the handful of facts that change how you are treated in an emergency β blood group, genotype, allergies, current medications, chronic conditions β travel with you as a scannable QR code and can be cryptographically verified by a health worker so a first responder can trust them on the spot.
This repository (lafiya-contracts) contains only the Soroban smart contract layer. The patient-facing web app and the docs/threat-model materials live in separate repos β see Lafiya Organization below.
In Nigeria, health records are paper, siloed per facility, and effectively lost the moment a patient moves, is referred, or arrives unconscious. In an emergency, the facts that decide treatment β especially genotype (AS/SS sickle-cell status), blood group, and drug allergies β are usually unknown to whoever is treating you, and wrong assumptions cost lives.
Even once that data is digitized (in lafiya-web), a responder still has no way to know whether a card's contents were ever checked by a real health worker. Without an independent, tamper-evident verification layer:
- Responders can't trust the data β anyone could edit a public emergency page, so a "verified" label is meaningless unless it's backed by something the patient (or an attacker) can't forge
- Health workers have no portable proof of their verification work β nothing links a specific attester to a specific record across systems
- Community health workers (CHWs) can't be paid reliably for last-mile registration and verification without a transparent, low-fee settlement rail
- Attests β records, on-chain, that a licensed health worker verified a specific patient record at a specific time, without storing any health data itself
- Allowlists β maintains the set of health workers authorized to submit attestations, so a "verified" indicator on a card actually means something
- Anchors trust β gives
lafiya-webandlafiya-verifiera single, independently checkable source of truth that a responder's QR scan can query directly
- Attestation registry (Soroban) β when a licensed health worker verifies a record, an on-chain attestation stores a hash of the record + the attester's identity + a timestamp β never the health data itself
- Attester allowlist β only allowlisted attesters can write to the registry, so verification can't be forged by an arbitrary wallet
- Hash-only on-chain footprint β personal data lives in
lafiya-web's encrypted, access-controlled off-chain database; Stellar holds only hashes, attestations, and payments - USDC incentive rails β CHWs are paid micro-amounts on Stellar per verified registration; near-zero fees and stablecoin settlement make last-mile outreach economically viable
- Transparent funding β grant and donor funds flow on-chain into the CHW incentive pool, so every dollar maps to a countable number of verified cards
graph TB
subgraph OffChain["Off-chain (lafiya-web)"]
PROFILE[Patient profile β Supabase]
CARD[Public emergency page + QR]
end
subgraph Contracts["lafiya-contracts (Soroban)"]
ALLOW[Attester allowlist]
REG[Attestation registry]
end
subgraph Actors["Actors"]
CHW[Community health worker\nlicensed attester]
RESP[Responder / clinician]
DONOR[Grant / donor funds]
end
PROFILE --> CARD
PROFILE -->|hash of record| REG
CHW -->|submits attestation| REG
ALLOW -->|checks attester is licensed| REG
REG -->|hash + attester id + timestamp| CARD
RESP -->|scans QR, checks attestation| CARD
DONOR -->|USDC incentive pool| CHW
attester-registryβ the on-chain allowlist of health workers authorized to write attestationsattestation-registryβ the on-chain record of which attester verified which record hash, and when; calls intoattester-registryon every writemultisig-accountβ a reusable N-of-M Soroban account contract that secures both registries' admin authorization
All three are implemented and unit-tested (target milestone M1, see Roadmap); none has been deployed to testnet yet.
Three Soroban contracts, each in its own crate under contracts/.
Design principle: no personal health data ever touches the blockchain. Personal data lives in lafiya-web's encrypted, access-controlled off-chain database. Stellar holds only hashes, attestations, and payments. This is what keeps Lafiya both privacy-respecting and regulator-compatible.
| Function | Description |
|---|---|
initialize(admin: Address) |
Sets the admin. Callable once. |
get_admin() -> Address |
Returns the current admin address. |
propose_admin(new_admin: Address) |
Proposes a new admin. Requires admin auth. |
accept_admin() |
Finalizes the admin transfer. Requires proposed/pending admin auth. Emits AdminTransferred. |
add_attester(attester: Address) |
Allowlists attester. Requires admin auth. Blocked while paused (Error::ContractPaused). Emits AttesterAdded. |
add_attester_with_info(attester: Address, license_hash: Option<BytesN<32>>, region: Option<Symbol>) |
Allowlists attester with optional metadata. Requires admin auth. Blocked while paused (Error::ContractPaused). Emits AttesterAdded. |
remove_attester(attester: Address) |
Removes attester from the allowlist. Requires admin auth. Blocked while paused (Error::ContractPaused). Emits AttesterRemoved. |
is_attester(attester: Address) -> bool |
Whether attester is currently allowlisted (and not suspended). Open to any caller, including other contracts. Callable while paused. |
get_attester_info(attester: Address) -> Option<AttesterInfo> |
Returns stored metadata for an allowlisted attester. Callable while paused. |
suspend_attester(attester: Address) |
Suspends an allowlisted attester without removing it. Requires admin auth. Blocked while paused (Error::ContractPaused). Emits AttesterSuspended. |
reinstate_attester(attester: Address) |
Reinstates a suspended attester. Requires admin auth. Blocked while paused (Error::ContractPaused). Emits AttesterReinstated. |
set_max_attesters(max_attesters: u32) |
Sets the soft cap on the number of allowlisted attesters. Requires admin auth. Does not evict existing attesters if lowered below the current count. |
get_max_attesters() -> u32 |
The current soft cap on the number of allowlisted attesters. |
get_attester_count() -> u32 |
The current number of allowlisted attesters. |
pause() |
Blocks add_attester, add_attester_with_info, remove_attester, suspend_attester, and reinstate_attester until unpaused. Requires admin auth. Emits Paused. |
unpause() |
Restores normal operation after pause. Requires admin auth. Emits Unpaused. |
is_paused() -> bool |
Whether the contract is currently paused. Callable while paused. |
get_schema_version() -> u32 |
Storage schema version recorded for the instance. Open to any caller. |
upgrade(new_wasm_hash: BytesN<32>) |
Replaces the contract's code with the already-uploaded wasm blob at new_wasm_hash. Requires admin auth; storage is untouched. See Contract upgrades. |
migrate() |
Runs any pending storage-schema migration, then records the new schema version. Requires admin auth; errors with MigrationNotRequired when nothing is pending. |
| Function | Description |
|---|---|
initialize(admin: Address, attester_registry: Address) |
Sets the admin and the attester-registry contract to consult. Callable once. |
get_admin() -> Address |
Returns the current admin address. |
get_attester_registry() -> Address |
Returns the configured attester-registry contract address. |
propose_admin(new_admin: Address) |
Proposes a new admin. Requires admin auth. |
accept_admin() |
Finalizes the admin transfer. Requires proposed/pending admin auth. Emits AdminTransferred. |
set_attester_registry(new_registry: Address) |
Repoints the attester-registry contract this registry consults for allowlist checks. Requires admin auth. Emits AttesterRegistryRepointed. |
pause() |
Blocks attest until unpaused. Requires admin auth. Emits Paused. |
unpause() |
Restores normal operation after pause. Requires admin auth. Emits Unpaused. |
is_paused() -> bool |
Whether the contract is currently paused. Callable while paused. |
attest(attester: Address, record_hash: BytesN<32>) -> Attestation |
Requires attester's auth and that attester is allowlisted (checked via a cross-contract call to attester-registry::is_attester). Stores { attester, timestamp } keyed by record_hash, keeping a bounded history per hash. Blocked while paused (Error::ContractPaused). Emits AttestationRecorded. |
revoke_attestation(record_hash: BytesN<32>) |
Revokes all attestations for record_hash. Requires admin auth. Emits AttestationRevoked. |
get_attestation(record_hash: BytesN<32>) -> Option<Attestation> |
Looks up the latest attestation for a record hash. Open to any caller β this is what lets a responder's QR scan verify a card without an external oracle. |
get_attestation_history(record_hash: BytesN<32>) -> Vec<Attestation> |
Returns the full bounded attestation history for a record hash, oldest first. Open to any caller. |
attester-registry is upgradeable by its admin (upgrade/migrate/get_schema_version
above), with storage schema versioning (SCHEMA_VERSION starts at 1) to make
schema-changing upgrades explicit and verifiable. attestation-registry does not
currently expose an upgrade/migrate path. Operators must follow
docs/runbooks/contract-upgrade.md β it covers the
pre-upgrade checklist, the upgrade() call sequence, verifying the wasm hash against
reviewed source, and migrate() handling for storage-schema-changing upgrades. The
mechanical steps are automated by scripts/upgrade.sh.
| Function | Description |
|---|---|
__constructor(signers: Vec<BytesN<32>>, threshold: u32) |
Configures the ed25519 signer set and required N-of-M threshold at deployment. |
__check_auth(...) |
Verifies ordered, unique signatures from configured signers whenever another contract calls require_auth() for this account address. |
attestation-registry calls attester-registry through a local #[contractclient] trait interface (just is_attester), not a direct crate dependency β depending on the whole crate would link attester-registry's own contract implementation into attestation-registry's wasm build too, which is both wasted size and, at least on the Soroban SDK version this repo pins, produces a linker warning from the two contracts' colliding initialize exports.
bindings/
βββ attestation-registry/ # generated TS client for attestation contract
βββ attester-registry/ # generated TS client for allowlist contract
contracts/
βββ multisig-account/ # reusable N-of-M admin account
β βββ Cargo.toml
β βββ src/
β βββ lib.rs
β βββ test.rs
β βββ integration_test.rs
βββ attester-registry/ # allowlist contract
β βββ Cargo.toml
β βββ src/
β βββ lib.rs # initialize, add_attester, remove_attester, is_attester, upgrade, migrate, get_schema_version
β βββ test.rs
βββ attestation-registry/ # attestation contract
βββ Cargo.toml
βββ src/
βββ lib.rs # initialize, attest, get_attestation, upgrade, migrate, get_schema_version
βββ test.rs
docs/
βββ adr/ # architecture decisions, index, and template
Cargo.toml # workspace + release profile
Cargo.lock # committed for reproducible builds
CHANGELOG.md # release notes incl. schema-impact statements
rust-toolchain.toml # pins stable + wasm32v1-none
Makefile # build/test/fmt/clippy/wasm/bindings/check
.github/workflows/ci.yml # runs the same checks on push/PR
LICENSE # MIT
CONTRIBUTING.md # local dev workflow
Client bindings are generated from the built WASM contracts using the stellar-cli tool. They allow frontend applications (like lafiya-web) to interact with the deployed contracts with full type safety.
To generate the bindings, run:
make bindingsThis builds the contracts and outputs TypeScript packages to the bindings/ directory:
bindings/attester-registrybindings/attestation-registry
To compile the generated packages:
cd bindings/attester-registry && npm install && npm run build
cd ../attestation-registry && npm install && npm run buildThe generated bindings are committed directly to this repository under the bindings/ directory. lafiya-web (or any other consumer) can consume them via:
- Direct git path dependency in
package.jsonpointing to the repo or subdirectory. - A git submodule in the consuming project.
- Alternatively, CI/CD can be configured to publish these directories as packages to the
@lafiyanpm organization.
- On-chain: Soroban smart contracts (Rust),
soroban-sdk25.x, on Stellar; USDC on Stellar for CHW payments - Network: Stellar testnet first
- Standards informing design: W3C Verifiable Credentials data model (issuer/holder/verifier roles, hash-based attestation)
git clone https://github.com/Lafiya-xyz/Lafiya-contract.git
cd Lafiya-contract
rustup target add wasm32v1-none # also picked up automatically via rust-toolchain.toml
make check # fmt-check + clippy + test + wasm buildDeploy multisig-account first with the ed25519 public keys of all M administrators and the required threshold N. For example, three signer keys with a threshold of two creates a 2-of-3 admin account. Keep the signer keys in separate custody and order submitted signatures by public key.
Use the deployed multisig contract address as admin when initializing both registries:
attester-registry.initialize(multisig_address)
attestation-registry.initialize(multisig_address, attester_registry_address)
The registry contracts need no multisig-specific logic. Their existing admin.require_auth() calls invoke the account contract's __check_auth, so an admin operation succeeds only when its authorization entry contains at least N valid signatures.
Authorization scope:
multisig-accountis a general-purpose N-of-M account. It does not inspect Soroban's authorization contexts or restrict the contract, function, arguments, asset movement, or nested invocations that a valid quorum may approve. It is not a registry-scoped or least-privileged account.
For pre-alpha use, assign a signer set dedicated exclusively to Lafiya registry administration; do not reuse those keys or that quorum for treasury or unrelated authority. Do not use the multisig address as a treasury: keep only the bounded XLM fee reserve recorded for the deployment and sweep any excess. Before signing, each signer must inspect the decoded authorization tree and independently verify the registry address, function, arguments, asset movements, and sub-invocations. A payload hash or transaction label alone is not sufficient.
The deployment record must contain the signer-set identifier (never secret keys), threshold, approved registry addresses, fee-reserve ceiling, and balance-sweep and authorization-review procedures. This account must not administer a production or mainnet deployment until its unscoped authority is explicitly accepted for that environment or replaced by an on-chain scoping policy. See ADR-0007 for the decision and residual risks.
Not yet deployed to testnet β deployment scripts and instructions land with the rest of milestone M1.
- Nigeria Data Protection Act (2023) governs all personal data held across the Lafiya project. Consent, encryption, and minimal disclosure are designed in from day one.
- No health data is ever written on-chain β only non-reversible hashes and attestations, by design (see Smart Contract Layer).
- M0 β Public card (testnet). One patient can create a profile and expose a working read-only emergency page via QR. (
lafiya-web) - M1 β Attestation. Soroban registry lets an allowlisted attester verify a record; the card shows a verified indicator. β this repo β contracts implemented and unit-tested; testnet deployment and
lafiya-webintegration still open. - M2 β Incentives. USDC-on-Stellar payout to a CHW per verified registration. Target custody architecture (separate treasury from registry admin, bounded payout contract) specified in ADR-0009; no payment contract implemented yet.
- M3 β Pilot. Small supervised field pilot; measure verified cards created and scan events.
- M4 β Mainnet + funding. Launch on mainnet; open transparent funding pool.
Stellar/Soroban does two things Lafiya genuinely needs that a plain web app cannot: it makes verification tamper-evident and independently checkable without exposing data, and it moves stablecoin micropayments to health workers cheaply and across borders. Remove Stellar and the trust layer and the incentive engine both disappear β Soroban is core to Lafiya, not shoehorned in.
make test # Unit tests (in-process soroban-sdk testutils)
make test-integration # Integration tests (deployed WASMs on local Soroban network)Covers, per contract (see contracts/*/src/test.rs and tests/integration/run.sh):
- β Initialize / double-initialize rejection
- β
Admin-gated writes (
add_attester,remove_attester), including rejection when the caller's auth entry doesn't match - β
Allowlist lookups (
is_attester) - β
attestby an allowlisted vs. non-allowlisted attester, and before the contract is initialized - β
get_attestationlookups, including unknown hashes and re-attestation overwrite - β
Emitted events (
AttesterAdded,AttesterRemoved,AttestationRecorded) - β Multisig threshold, signer validation, signature ordering, and invalid-signature rejection
- β Multisig-backed initialization and admin operations through the contract-account authorization path
- Rust (stable) +
wasm32v1-nonetarget β seerust-toolchain.toml soroban-sdk25.x- Stellar testnet account and USDC trustline, once deployment scripts land
MIT.
Contributions are welcome! As an open-source Digital Public Good, we rely on community contributions to build and maintain Lafiya.
Please refer to CONTRIBUTING.md for our detailed guidelines, which cover:
- Local development environment setup
- Branching and commit conventions (Conventional Commits)
- Cross-repo shared-contract coordination guidelines
- Database/Supabase migration details
- Smart contract quality standards and testing checklist
This repository specifically needs collaborators with experience in:
- Stellar / Soroban smart contract development (Rust)
- On-chain data modeling and attestation/verifiable-credential design
This repo is one of five in the lafiya-xyz organization.
| Repo | URL | Purpose | Priority |
|---|---|---|---|
lafiya-web |
github.com/Lafiya-xyz/lafiya-web | Patient + responder web app (Next.js). Public emergency page, authed profile editor, QR generation. | Build first |
lafiya-contracts (this repo) |
github.com/Lafiya-xyz/Lafiya-contract | Soroban smart contracts (Rust): attestation registry + attester allowlist. Testnet first. | Build next |
lafiya-docs |
github.com/Lafiya-xyz/lafiya-docs | Concept note, data model, threat model, privacy design, funding/DPG materials, references. | Start now (lightweight) |
.github |
github.com/Lafiya-xyz/.github | Organization profile README and contribution guidelines. | Start now |
lafiya-verifier |
github.com/Lafiya-xyz/lafiya-verifier | CHW verification tool. Begins as a route inside lafiya-web; split out only if it grows. |
Later |
Resist scaffolding empty repos. Two working repos (
lafiya-web,lafiya-contracts) beat five half-built ones. Build one honest milestone at a time.
lafiya-web ββ(record hash)βββΆ lafiya-contracts
β
CHW attests ββ(licensed?)βββΆ β (attester allowlist check)
βΌ
attestation: hash + attester id + timestamp
β
βΌ
lafiya-web public emergency page
β
βΌ
responder scans QR, sees verified indicator
lafiya-webholds the patient's private profile and computes a hash of the emergency-relevant record.- A licensed CHW, verified against the attester allowlist, submits an attestation to the attestation registry in this repo β a hash, the attester's identity, and a timestamp, never the health data itself.
lafiya-web's public emergency page reads the attestation to show a verified indicator; a responder scanning the QR can independently trust it without an external oracle.lafiya-verifier(later) gives CHWs a dedicated flow for step 2 as it splits out oflafiya-web.
Attestation schema β a hash of the record + the attester's identity + a timestamp, defined by the contracts in this repo and consumed by lafiya-web's public emergency page. If the shape of an attestation changes here, lafiya-web's verification-display logic must be updated in the same change set (or a tracked follow-up opened there).
- Treat this section as the source of truth for cross-repo contracts. Each repo's own README covers repo-local conventions.
- The contracts are implemented and unit-tested but not yet deployed to testnet β don't assume a live contract ID or deployment scripts exist; check Repository Structure before referencing a path.
- When a change here affects the attestation schema or either contract's function signatures, call it out explicitly so
lafiya-webcan be updated to match.
For issues and questions:
- GitHub Issues: Create an issue
Lafiya is an information aid, not a medical device and not a substitute for professional medical judgment. Verified indicators reflect that a record was attested by a registered health worker; they are not a clinical guarantee. Treatment decisions remain the responsibility of the attending clinician.
Found a vulnerability? Please don't open a public issue β see SECURITY.md for how to report it privately.
These works directly informed Lafiya's design and are the intended reading for contributors, particularly the attestation/trust-layer work in this repo.
Books
- Preukschat, A., & Reed, D. (2021). Self-Sovereign Identity: Decentralized Digital Identity and Verifiable Credentials. Manning. β The blueprint for Lafiya's attestation layer: issuer/holder/verifier roles, verifiable credentials, hash-based attestation, key management, and offline verification.
- Kleppmann, M. (2017). Designing Data-Intensive Applications. O'Reilly. β Informs the boundary between what lives in the off-chain database and what is anchored on-chain.
- Martin, R. C. (2017). Clean Architecture: A Craftsman's Guide to Software Structure and Design. Prentice Hall. β Discipline for an AI-assisted codebase: clear boundaries so the contracts, app, and data layer stay independently maintainable.
- Shortliffe, E. H., & Cimino, J. J. (Eds.). (2021). Biomedical Informatics: Computer Applications in Health Care and Biomedicine (5th ed.). Springer. β Grounds which fields are decision-relevant in an emergency, informing what a record hash here actually represents.
- Toyama, K. (2015). Geek Heresy: Rescuing Social Change from the Cult of Technology. PublicAffairs. β Keeps the project honest: the attestation layer amplifies trust in community health workers rather than replacing them.
Standards & documentation
- Stellar Development Foundation β Stellar and Soroban developer documentation.
- W3C β Verifiable Credentials Data Model.
- Nigeria Data Protection Act (2023) β Nigeria Data Protection Commission.
- Digital Public Goods Alliance β DPG Standard.
Lafiya β Your vitals, verified.
Built for the Stellar ecosystem. Open source. Community owned.