MedFi is a decentralized platform built on Ethereum to facilitate secure, transparent, and trustless interactions between patients and verified medical professionals. It leverages smart contracts to manage medical service bookings, USDC-based escrow payments, medical records with subscription-based access, and professional ratings. The platform consists of three main smart contracts:
- MedFiRegistry: Manages the registration, verification, and blacklisting of hospitals and medical professionals.
- MedFiEscrow: Handles secure USDC payment escrow for medical services with fee collection.
- MedFiRecords: Stores medical record hashes with subscription-based access and patient-provider ratings.
MedFi aims to:
- Ensure Trust: Verify hospitals and medical professionals through an admin-based system.
- Secure Payments: Use USDC-based escrow to protect patients and providers during transactions.
- Maintain Privacy: Store medical record hashes on-chain while keeping sensitive data off-chain.
- Enable Transparency: Allow patients to rate verified professionals and access their average ratings.
- Prevent Fraud: Implement blacklisting, verification, and activity tracking mechanisms.
- Sustainable Access: Monthly subscription model for medical record storage and management.
The MedFiRegistry contract serves as the backbone for managing and verifying hospitals and medical professionals on the platform.
Admin Management:
- Owner can add or remove admins for decentralized administration.
- Admins can verify/unverify hospitals and professionals.
- Admins can blacklist/unblacklist both hospitals and professionals.
Hospital Registration:
- Hospitals self-register on-chain (metadata stored off-chain).
- Admins verify hospitals after registration review.
- Verified hospitals can perform platform activities.
- Blacklisting automatically deactivates and unverifies hospitals.
Professional Registration:
- Professionals self-register on-chain (metadata stored off-chain).
- Admins verify professionals after registration review.
- Professionals can update their profile information (metadata stored off-chain).
- Supports batch verification/unverification operations.
Activity Verification:
canPerformActivities(): Checks if an address (hospital or professional) is verified, active, and not blacklisted.- Used by other contracts to validate professional/hospital status before allowing actions.
Blacklisting:
- Admins can blacklist professionals or hospitals to prevent all platform activities.
- Blacklisting automatically deactivates and unverifies the entity.
- Can be reversed with unblacklisting, which reactivates the entity.
Enumeration:
- Track all registered professionals and hospitals.
- Get total counts and full address lists.
- Useful for frontend dashboards and analytics.
Hospital:
struct Hospital {
bool isVerified;
uint256 registrationTimestamp;
bool isActive;
}Professional:
struct Professional {
address professionalAddress;
bool isVerified;
uint256 registrationTimestamp;
bool isActive;
}Admin Management:
addAdmin(address): Owner adds admin.removeAdmin(address): Owner removes admin.
Hospital Functions:
registerHospital(): Self-registration for hospitals.verifyHospital(address): Admin verifies a hospital.unverifyHospital(address): Admin revokes hospital verification.blacklistHospital(address): Admin blacklists hospital.unblacklistHospital(address): Admin removes hospital from blacklist.isVerifiedHospital(address): Check if hospital is verified and active.getHospital(address): Get hospital details.getAllHospitals(): Get all registered hospital addresses.getTotalHospitals(): Get total number of hospitals.
Professional Functions:
registerProfessional(): Self-registration for professionals.updateProfessionalProfile(): Professional updates their profile (off-chain metadata).verifyProfessional(address): Admin verifies professional.verifyMultipleProfessionals(address[]): Admin verifies multiple professionals at once.unverifyProfessional(address): Admin revokes professional verification.unverifyMultipleProfessionals(address[]): Admin unverifies multiple professionals at once.blacklistProfessional(address): Admin blacklists professional.unblacklistProfessional(address): Admin removes professional from blacklist.isVerifiedProfessional(address): Check if professional is verified and active.getProfessional(address): Get professional details.getAllProfessionals(): Get all registered professional addresses.getTotalProfessionals(): Get total number of professionals.
General Functions:
canPerformActivities(address): Check if address can perform platform activities (verified, active, not blacklisted).
The MedFiEscrow contract manages secure USDC payments for medical services using an escrow mechanism with percentage-based fees.
USDC-Based Payments:
- All transactions use USDC stablecoin for price stability.
- Immutable USDC token address set at deployment.
- Uses SafeERC20 for secure token transfers.
Booking Services:
- Patients book services with verified providers using USDC.
- Requires USDC approval before booking.
- Percentage-based fee is deducted (configurable, max 100%).
- Remaining amount held in escrow until confirmation.
- Enhanced verification checks provider status via registry.
Service Confirmation:
- Patients confirm service completion to release funds.
- Reentrancy protection on all fund transfers.
- Updates provider statistics (confirmed bookings, total earnings).
- Verifies provider can still perform activities at confirmation time.
Refund Mechanisms:
- Provider Refund: Provider can refund unconfirmed bookings.
- Emergency Refund: Owner can refund any booking for dispute resolution.
- Note: Standard refunds return amount after fees; emergency refunds should ideally return full original amount.
Fee Management:
- Owner can update fee percentage.
- Owner withdraws collected fees in USDC.
- Tracks total fees collected.
Provider Statistics:
- Tracks confirmed bookings per provider.
- Tracks total earnings per provider in USDC.
getProviderStats(): Returns comprehensive provider statistics.
Contract Statistics:
- Total bookings count.
- Total fees collected.
- Current USDC balance.
- Current fee percentage.
Booking:
struct Booking {
address patient;
address provider;
uint256 amount; // Amount after fees
uint256 originalAmount; // Original amount before fees
bool isPaid;
bool confirmed;
uint256 timestamp;
}ReentrancyGuardon all fund transfers.- Validates provider via
registry.canPerformActivities(). - Checks for blacklisted providers.
- Validates USDC allowance before booking.
- Custom errors for gas efficiency.
bookService(address provider, uint256 amount): Book service with USDC.confirmService(uint256 bookingId): Patient confirms and releases payment.refundPatient(uint256 bookingId): Provider refunds unconfirmed booking.emergencyRefund(uint256 bookingId): Owner emergency refund.withdrawFees(): Owner withdraws collected fees.updateFeePercentage(uint256): Owner updates fee percentage.getProviderStats(address): Get provider statistics.getContractStats(): Get overall contract statistics.
The MedFiRecords contract manages medical records with a subscription-based access model and professional ratings system.
Subscription-Based Access:
- Monthly Subscription: 1 USDC per 30 days.
- Required for patients to:
- Add their own medical records.
- View their medical records.
- Grant/revoke professional access.
- Automatic renewal when performing subscription-required operations.
- Manual renewal option to extend subscription.
- Subscription fee sent to configurable fee recipient.
Access Control System:
- Patients grant access to specific professionals.
- Only granted professionals can view/add patient records.
- Patients can revoke access at any time.
- Professionals must be verified and not blacklisted to receive access.
Medical Record Storage:
- By Healthcare Professionals:
- Must have patient's granted access.
- Must pass
canPerformActivities()check. - Automatically triggers patient subscription renewal if needed.
- By Patients:
- Patients can add their own records.
- Requires active subscription.
- Records stored as hashes (actual data off-chain).
- Gas-optimized storage using record IDs.
- Tracks professional activity (record count per professional).
Rating System:
- Patients can rate verified professionals (1-5 scale).
- One rating per patient-professional pair.
- Rating count limited by confirmed bookings from escrow contract.
- Calculates average ratings.
- Provides detailed rating statistics.
Subscription Management:
- Tracks last payment timestamp and active status.
- Calculates expiry time (lastPayment + 30 days).
- Provides detailed subscription status functions.
- Shows days/hours remaining.
MedicalRecord:
struct MedicalRecord {
address patient;
address professional; // Address(0) if added by patient
string recordHash; // IPFS or other hash
uint96 timestamp; // Gas-optimized timestamp
bool addedByProfessional;
}mapping(uint256 => MedicalRecord): Main record storage by ID.mapping(address => uint256[]): Patient's record IDs array.mapping(address => mapping(address => bool)): Access grants.mapping(address => uint256): Last payment timestamp.mapping(address => bool): Active subscription status.
Subscription Management:
renewSubscription(): Manually renew subscription for 30 days.checkPatientSubscriptionStatus(address): Check balance, allowance, and subscription status.getSubscriptionExpiry(address): Get expiry timestamp.getDaysRemaining(address): Get days left in subscription.isSubscriptionActive(address): Check if currently active.
Access Control:
grantAccess(address professional): Grant professional access (requires subscription).revokeAccess(address professional): Revoke professional access.hasAccess(address patient, address professional): Check access status.
Record Management:
storeRecordByCareProvider(address patient, string recordHash): Professional adds record.addMedicalRecord(string recordHash): Patient adds own record.getMyMedicalRecords(): Patient views their records (requires subscription).getPatientMedicalRecords(address patient): Professional views patient records (requires access).getMedicalRecord(uint256 recordId): Get specific record by ID.getPatientRecordCount(address): Get total record count.
Rating System:
rateHealthWorker(address healthWorker, uint8 rating): Rate professional (1-5).getAverageRating(address): Get average rating.getHealthWorkerRatingDetails(address): Get detailed rating info.
Statistics:
getProfessionalStats(address): Get professional record count and activity status.getTotalRecords(): Get total records in system.
- Subscription checks prevent unauthorized access.
- Enhanced professional verification via registry.
- Blacklist checks for additional security.
- Access control modifiers (
hasPatientAccess,requiresActiveSubscription). - SafeERC20 for secure USDC transfers.
MedFiRegistry (Central Authority):
- Provides
canPerformActivities(address)for all contracts. - Provides
isBlacklisted(address)for security checks. - Provides
isVerifiedProfessional(address)andisVerifiedHospital(address). - Returns professional and hospital details.
MedFiEscrow → MedFiRegistry:
- Validates providers during booking.
- Validates providers during confirmation.
- Uses
canPerformActivities()andisBlacklisted().
MedFiRecords → MedFiRegistry:
- Validates professionals when granting access.
- Validates professionals when storing records.
- Uses
canPerformActivities()for all professional checks.
MedFiRecords → MedFiEscrow:
- Checks
getConfirmedBookingsCount()to validate ratings. - Ensures ratings align with actual service delivery.
MedFiRecords → USDC Token:
- Collects subscription fees (1 USDC per 30 days).
- Uses SafeERC20 for secure transfers.
- Transfers to configurable fee recipient.
Hospital Registration:
1. Hospital calls registerHospital()
2. Admin verifies hospital via verifyHospital(hospitalAddress)
3. Hospital can now perform platform activities
Professional Registration:
1. Professional calls registerProfessional()
2. Professional updates profile via updateProfessionalProfile() (optional)
3. Admin verifies professional via verifyProfessional(professionalAddress)
4. Professional can now perform platform activities
1. Patient approves USDC spending to MedFiRecords contract
2. Patient calls renewSubscription() or performs any subscription-required action
3. 1 USDC transferred to fee recipient
4. Subscription active for 30 days
1. Patient ensures active subscription
2. Patient calls grantAccess(professionalAddress)
3. Professional can now view and add patient records
1. Patient approves USDC to MedFiEscrow contract
2. Patient calls bookService(providerAddress, usdcAmount)
3. Fee deducted, remaining held in escrow
4. Provider delivers service
5. Patient calls confirmService(bookingId)
6. USDC released to provider
7. Provider stats updated
By Professional:
1. Professional has patient access
2. Professional calls storeRecordByCareProvider(patientAddress, recordHash)
3. Patient subscription auto-renewed if needed
4. Record stored with professional attribution
By Patient:
1. Patient has active subscription
2. Patient calls addMedicalRecord(recordHash)
3. Record stored with patient attribution
1. Patient has confirmed booking with professional
2. Patient calls rateHealthWorker(professionalAddress, rating)
3. Rating validated against confirmed bookings
4. Average rating updated
- Ownable: Contract owner controls critical functions.
- Admin System: Distributed administration for verification.
- Self-Registration: Hospitals and professionals can register themselves.
- Admin Verification: Required before entities can perform platform activities.
- Activity Checks:
canPerformActivities()validates all professional/hospital actions.
- ReentrancyGuard: Prevents reentrancy attacks on fund transfers.
- SafeERC20: Prevents token transfer vulnerabilities.
- Allowance Checks: Validates sufficient USDC approval before operations.
- Fee Limits: Fee percentage capped at 100%.
- Immutable USDC: Token address cannot be changed after deployment.
- Verification System: Admin verification required for all entities.
- Blacklisting: Emergency mechanism to block malicious actors.
- Access Control: Patient-controlled access to medical records.
- Rating Validation: Ratings tied to confirmed bookings.
- Subscription Enforcement: Access control via payment requirements.
All contracts emit comprehensive events for:
- Registration and verification actions.
- Payment flows and confirmations.
- Access grants and revocations.
- Rating submissions.
- Subscription renewals.
- Fee withdrawals and recipient changes.
- Custom errors instead of require strings.
- Packed timestamp (uint96) in medical records.
- Record ID system instead of storing full records in arrays.
- Immutable variables where possible.
- Mapping-based tracking for array membership.
- Unbounded loops in
_getMedicalRecordsForPatient()- could hit gas limit with many records. - Dynamic arrays in storage (
patientRecordIds,professionalAddresses,hospitalAddresses) - expensive for large datasets. - Batch operations could exceed block gas limit with too many items.
- Implement pagination for record retrieval.
- Consider off-chain indexing for large datasets.
- Add limits to batch operations.
- Use events for historical data tracking rather than storage queries.
- OpenZeppelin contracts:
^0.8.0or compatible version - Solidity:
^0.8.0 - USDC token contract address on target network
-
Deploy MedFiRegistry
- No constructor parameters needed
- Deployer becomes owner and first admin
-
Deploy MedFiEscrow
- Constructor:
(registryAddress, usdcAddress, feePercent) - Example:
(0x123..., 0xUSDC..., 5)for 5% fee
- Constructor:
-
Deploy MedFiRecords
- Constructor:
(registryAddress, escrowAddress, usdcAddress) - Set fee recipient if different from deployer
- Constructor:
- Add additional admins to registry if needed.
- Set appropriate fee recipient in MedFiRecords.
- Verify all contract addresses on block explorer.
- Test complete workflow on testnet.
- Mainnet: Full security but high gas costs.
- Layer 2 (Optimism, Arbitrum, Base): Lower costs, good for production.
- Testnets: Sepolia, Goerli for testing.
Ensure correct USDC address for your network:
- Ethereum Mainnet: Different than testnet
- L2 networks: Native or bridged USDC
- Testnets: Faucet USDC for testing
- Fee Loss on Refunds: Regular refunds only return amount after fees. Consider implementing full refund logic.
- Stuck Funds: Unconfirmed/unrefunded bookings lock USDC indefinitely. Consider time-based auto-refunds.
- Fixed Subscription: All patients pay same rate regardless of usage.
- No Dispute Resolution: No formal mechanism beyond emergency refunds.
- No Booking Cancellation: Patients cannot cancel bookings, only providers can refund.
- Rating Gaming: Current system allows one rating per confirmed booking globally, not per patient-provider pair.
- No Time Limits: Bookings can remain pending indefinitely.
- No Affiliation System: Professionals are not linked to hospitals on-chain.
- Read Operations Charge Fee:
getMyMedicalRecords()can trigger subscription renewal (not a pure view function). - Centralization: Single owner controls emergency functions and admin appointments.
- No Multi-Token Support: Locked to USDC only.
- Unbounded Arrays: Could hit gas limits with many records or entities.
- Implement proper fee refund logic for all refund scenarios.
- Add booking time limits and auto-cancellation.
- Fix rating system to properly track per patient-provider pair.
- Separate read operations from payment requirements.
- Add formal dispute resolution mechanism.
- Implement tiered subscription models.
- Support multiple stablecoin options.
- Add pagination for record retrieval.
- Implement multi-sig for admin operations.
- Consider hospital affiliation system if needed.
- DAO governance for decentralization.
- Reputation scoring beyond ratings.
- Integration with health insurance systems.
- Off-chain computation for complex analytics.
- Hospital registration and verification flow
- Professional self-registration and verification
- Professional profile updates
- Batch verification operations
- Blacklisting and unblacklisting
- Unauthorized access attempts
- Enumeration functions (getAllProfessionals, getAllHospitals)
- canPerformActivities checks
- Booking with verified providers
- USDC approval and transfer
- Fee calculation accuracy
- Confirmation and payment release
- Provider refunds
- Emergency refunds
- Fee withdrawal
- Reentrancy attack attempts
- Booking with blacklisted providers
- Subscription renewal scenarios
- Access grant and revocation
- Record addition by professionals
- Record addition by patients
- Record retrieval permissions
- Rating submission and validation
- Expired subscription handling
- Insufficient USDC balance scenarios
MedFi is a comprehensive decentralized healthcare platform that addresses key challenges in medical service delivery through blockchain technology. The system provides:
- Trust: Admin-based verification system for hospitals and professionals
- Security: USDC-based escrow with reentrancy protection
- Privacy: On-chain record hashes with off-chain data storage
- Sustainability: Subscription model for ongoing access
- Transparency: On-chain ratings and statistics
- Scalability: Self-registration with admin verification workflow
The modular architecture allows for future enhancements while maintaining security and decentralization. The simplified registry system makes it easier for professionals to onboard while maintaining security through admin verification and blacklisting capabilities.
Thorough testing and security auditing are essential before production deployment.
For implementation details, refer to the smart contract code and ensure proper testing on testnets before mainnet deployment.
- MockUSDC: 0x1807F7c4984f5188e948C2e828fadE1b2F0011eb
- MedFi Registry: 0x6C24c00a08e088f84b7e72588509b93133BCEd5b
- MedFi Escrow: 0x6101308FD605f85b4ad1F5569c06eC9393c5a52A
- MedFi Records: 0xEd27133B24A9cDf08E2a9F05D4ba2B5f323E2dE1
For questions, issues, or contributions, please refer to the project repository or contact the development team.
Important: Always conduct thorough security audits before deploying to mainnet. This README reflects the current state of the contracts and should be updated as the system evolves.