Skip to content

MandalaChain/stunting-smartcontract

Repository files navigation

Stunting Smart Contract - Audit Registry

About the Project

This is a blockchain-integrated smart contract system designed to improve transparency, data integrity, and verifiability in stunting prevention programs. The contract provides an immutable audit trail for health data modifications, ensuring accountability and trust in data management systems.

Smart Contract Overview

This project implements a simplified, gas-efficient architecture focused on audit logging for health data systems.

AuditRegistry.sol

A lightweight, gas-efficient contract for recording data modifications on the blockchain:

  • Immutable Audit Trail: Records all data modifications using blockchain-native event logs
  • Zero On-Chain Storage: Maximum gas efficiency by leveraging event logs instead of storage
  • Minimal Blockchain Pattern: Only stores what absolutely needs to be on-chain
  • Gasless Transactions: Supports meta-transactions through EIP-712 signed messages, allowing relayers to pay gas fees
  • EIP-2771 Compatible: Standard implementation for trusted forwarder pattern

Key Features

  • Simplified Architecture: Single-purpose contract focused on audit logging
  • Gas Efficiency: Event-based logging reduces costs by up to 90% compared to storage operations
  • Gasless Transactions: Users can sign transactions without holding native tokens; relayers submit on their behalf
  • EIP-712 Support: Type-safe signing standard for better security and wallet integration
  • Zero Storage Cost: Leverages blockchain's immutable event log system
  • Verifiable & Transparent: All audit records are publicly verifiable on the blockchain
  • Future-Proof: Designed to integrate with off-chain indexing solutions (The Graph, Etherscan, custom indexers)

Installation

git clone https://github.com/MandalaChain/stunting-smartcontract.git
cd stunting-smartcontract
yarn install

Running the Project

Development Commands

# Compile contracts
yarn compile

# Run local Hardhat node (required for local deployment and testing)
yarn local-node

# In a new terminal, deploy to localhost
yarn deploy-localhost

# Test the logging functionality (after deployment)
yarn test-log --network localhost

# Run tests
yarn test

# Run extended tests
yarn test-extended

# Generate gas usage report
yarn test-gas

# Check code formatting
yarn format:check

# Auto-format code
yarn format

Network Deployment

The contract can be deployed to various networks as configured in hardhat.config.ts. Ensure you have:

  • Configured RPC endpoints in your environment
  • Sufficient funds in the deployer wallet
  • Updated network-specific parameters in the config files

Test Results

All tests pass successfully! The comprehensive test suite verifies:

  • ✅ Contract deployment and initialization
  • ✅ Direct logging functionality
  • ✅ Meta-transaction execution and validation
  • ✅ Error handling for invalid nonces and signatures
  • ✅ Event emission and data integrity
  • ✅ Gas optimization benchmarks

Gas usage is highly optimized through the event-based logging approach, making it cost-effective for high-frequency audit logging.

Example Usage

Direct Transaction (Standard Approach)

// Hash the data you want to record
const metadata = {
  childId: "123456",
  weight: "7.5",
  height: "63",
  measurementDate: "2023-05-12",
  location: "Puskesmas A"
};

// Create a unique identifier for this data entry
const resourceId = ethers.utils.keccak256(
  ethers.utils.toUtf8Bytes(JSON.stringify(metadata))
);

// Log the modification on-chain
const tx = await auditRegistry.logModification(resourceId);
const receipt = await tx.wait();

// The transaction hash serves as immutable proof
console.log("Audit record transaction:", tx.hash);
console.log("Block number:", receipt.blockNumber);
console.log("Gas used:", receipt.gasUsed.toString());

Gasless Transaction (Meta-Transaction Pattern)

This approach allows users to sign transactions without paying gas fees. A relayer submits the transaction on behalf of the user.

// 1. Prepare the function call data
const resourceId = ethers.utils.keccak256(
  ethers.utils.toUtf8Bytes(JSON.stringify(metadata))
);
const functionCall = auditRegistry.interface.encodeFunctionData(
  "logModification",
  [resourceId]
);

// 2. Get the current nonce for the user (prevents replay attacks)
const nonce = await auditRegistry.nonces(userAddress);

// 3. Create the meta transaction data structure
const metaTxValue = {
  from: userAddress,
  nonce: nonce.toNumber(),
  functionCall: functionCall
};

// 4. User signs the meta transaction (EIP-712 typed data)
const signature = await userWallet._signTypedData(
  metaTxDomain,
  metaTxTypes,
  metaTxValue
);

// 5. Relayer executes the meta transaction (pays gas fees)
const tx = await auditRegistry
  .connect(relayer)
  .executeMetaTransaction(
    metaTxValue.from,
    metaTxValue.nonce,
    metaTxValue.functionCall,
    signature
  );

await tx.wait();

// Result: Transaction is processed with relayer paying gas
// Important:
// - DataModified event shows the contract address as the actor
// - MetaTransactionExecuted event records both the original user and the relayer
// - This enables gasless user experience while maintaining accountability

Folder Structure

├── config/                                   # Configuration files
│   ├── AuditConfig.ts                 # Audit system configuration
│   └── ContractArguments.ts     # Deployment arguments
├── contracts/                              # Solidity smart contracts
│   ├── AuditRegistry.sol            # Main audit registry contract
│   ├── Interface/                         # Contract interfaces
│   │   └── IAuditRegistry.sol
│   └── utils/                                # Utility contracts
│       └── MetaTransaction.sol   # Meta-transaction support
├── lib/                                        # TypeScript libraries
│   ├── AuditConfigInterface.ts
│   ├── AuditRegistryProvider.ts
│   ├── NetworkConfigInterface.ts
│   └── Networks.ts
├── scripts/                                    # Deployment and testing scripts
│   ├── 1_deploy.ts                       # Main deployment script
│   ├── 2_deploy_localhost.ts       # Local deployment
│   └── 3_testLogModification.ts # Test logging functionality
├── test/                                         # Test suite
│   └── index.ts
├── typechain-types/            # Auto-generated TypeScript types
├── hardhat.config.ts           # Hardhat configuration
├── package.json                 # Dependencies and scripts
├── tsconfig.json                 # TypeScript configuration
├── DOCS.md                     # Additional documentation
├── PLAN.md                     # Project planning
└── README.md              # This file

API Reference (Smart Contract)

AuditRegistry Contract

Functions

logModification(bytes32 resourceId)

  • Description: Records a data modification event on the blockchain
  • Parameters:
    • resourceId: A unique identifier (hash) representing the modified data
  • Access: Public
  • Returns: Transaction receipt
  • Emits: DataModified(address indexed actor, bytes32 indexed resourceId)

executeMetaTransaction(address from, uint256 nonce, bytes functionCall, bytes signature)

  • Description: Executes a function call via meta transaction (gasless transaction)
  • Parameters:
    • from: The address of the original signer
    • nonce: Current nonce for replay protection
    • functionCall: Encoded function call data
    • signature: EIP-712 signature from the user
  • Access: Public
  • Returns: Transaction receipt
  • Emits: MetaTransactionExecuted(address indexed from, address indexed relayer, bytes functionCall)

nonces(address user)

  • Description: Gets the current nonce for a user address
  • Parameters:
    • user: The address to query
  • Returns: uint256 - Current nonce value
  • Access: Public view

Events

DataModified(address indexed actor, bytes32 indexed resourceId)

  • Emitted when: A data modification is logged
  • Parameters:
    • actor: The address that performed the modification (or contract address for meta-tx)
    • resourceId: The unique identifier of the modified data

MetaTransactionExecuted(address indexed from, address indexed relayer, bytes functionCall)

  • Emitted when: A meta transaction is successfully executed
  • Parameters:
    • from: The original signer of the transaction
    • relayer: The address that submitted the transaction
    • functionCall: The encoded function call that was executed

Integration with Backend

Workflow

  1. Data Modification: When data is created or updated in your backend system
  2. Hash Generation: Generate a unique resourceId by hashing relevant metadata
  3. Blockchain Logging: Call logModification(resourceId) to record the audit trail
  4. Transaction Receipt: Retrieve the transaction hash from the receipt
  5. Database Storage: Store the transaction hash alongside the data for verification
  6. Verification: Users can verify the audit trail using:
    • Blockchain explorers (Etherscan, etc.)
    • Direct RPC queries to the node
    • Event indexing services (The Graph, custom indexers)

Integration Example

// Backend service example
async function recordDataModification(data: HealthData) {
  // 1. Save data to database
  const savedData = await database.save(data);

  // 2. Generate resource ID
  const resourceId = ethers.utils.keccak256(
    ethers.utils.toUtf8Bytes(JSON.stringify({
      id: savedData.id,
      timestamp: savedData.updatedAt,
      checksum: savedData.checksum
    }))
  );

  // 3. Log to blockchain
  const tx = await auditRegistry.logModification(resourceId);
  const receipt = await tx.wait();

  // 4. Update database with blockchain proof
  await database.update(savedData.id, {
    auditTxHash: receipt.transactionHash,
    auditBlockNumber: receipt.blockNumber
  });

  return {
    dataId: savedData.id,
    txHash: receipt.transactionHash
  };
}

Verification Example

// Verify data integrity
async function verifyDataIntegrity(dataId: string) {
  const data = await database.findById(dataId);

  // Get transaction from blockchain
  const tx = await provider.getTransaction(data.auditTxHash);
  const receipt = await provider.getTransactionReceipt(data.auditTxHash);

  // Parse logs to get the DataModified event
  const event = receipt.logs
    .map(log => auditRegistry.interface.parseLog(log))
    .find(parsed => parsed.name === 'DataModified');

  // Reconstruct expected resource ID
  const expectedResourceId = ethers.utils.keccak256(
    ethers.utils.toUtf8Bytes(JSON.stringify({
      id: data.id,
      timestamp: data.updatedAt,
      checksum: data.checksum
    }))
  );

  // Verify
  return {
    verified: event.args.resourceId === expectedResourceId,
    blockNumber: receipt.blockNumber,
    timestamp: (await provider.getBlock(receipt.blockNumber)).timestamp
  };
}

Architecture Benefits

Why Event-Based Logging?

  1. Cost Efficiency: Events cost ~2,000 gas vs ~20,000 gas for storage operations
  2. Immutability: Events are permanent and cannot be altered or deleted
  3. Indexability: Events are easily indexed by blockchain explorers and custom indexers
  4. Scalability: Minimal on-chain footprint allows for high-volume logging

Security Considerations

  • Immutable Records: Once logged, audit trails cannot be modified or deleted
  • Cryptographic Proof: Transaction hashes provide cryptographic proof of logging
  • Replay Protection: Nonce-based system prevents meta-transaction replay attacks
  • Signature Verification: EIP-712 ensures secure, type-safe transaction signing
  • No PII On-Chain: Only hashes are stored, keeping sensitive data off-chain

Use Cases

  • Health data audit trails for stunting prevention programs
  • Supply chain tracking and verification
  • Document modification history
  • Compliance and regulatory reporting
  • Data integrity verification for sensitive records
  • Multi-party data sharing with accountability

Configuration

Environment Variables

Create a .env file in the root directory:

# Network RPC URLs
ETHEREUM_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY
BSC_RPC_URL=https://bsc-dataseed1.binance.org

# Deployer Private Key (NEVER commit this!)
PRIVATE_KEY=your_private_key_here

# Block Explorer API Keys (for verification)
ETHERSCAN_API_KEY=your_etherscan_key
POLYGONSCAN_API_KEY=your_polygonscan_key
BSCSCAN_API_KEY=your_bscscan_key

# Gas Configuration
GAS_PRICE=50
GAS_LIMIT=3000000

Network Configuration

Edit hardhat.config.ts to configure networks, gas settings, and compiler options.

Deployment

Deploy to Local Network

# Terminal 1: Start local node
yarn local-node

# Terminal 2: Deploy
yarn deploy-localhost

Deploy to Testnet/Mainnet

# Deploy to a specific network
npx hardhat run scripts/1_deploy.ts --network <network-name>

# Example: Deploy to Polygon Mumbai testnet
npx hardhat run scripts/1_deploy.ts --network mumbai

Post-Deployment

After deployment, the contract address will be saved in the deployment artifacts. You can verify the contract on block explorers:

npx hardhat verify --network <network> <contract-address>

Gas Optimization

The contract is optimized for minimal gas consumption:

  • logModification(): ~2,500 gas (event only, no storage)
  • executeMetaTransaction(): ~5,000 gas (signature verification + event)

For comparison, traditional storage-based audit logging would cost 10-20x more.

Troubleshooting

Common Issues

Issue: "Nonce too low" error in meta-transactions

  • Solution: Ensure you're fetching the latest nonce before signing

Issue: "Invalid signature" error

  • Solution: Verify the EIP-712 domain and types match the contract

Issue: Deployment fails with "insufficient funds"

  • Solution: Ensure the deployer wallet has enough native tokens

Issue: Events not showing in logs

  • Solution: Make sure you're waiting for the transaction receipt

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Guidelines

  • Write comprehensive tests for new features
  • Follow the existing code style (run yarn format)
  • Update documentation as needed
  • Ensure all tests pass before submitting PR

Acknowledgments

  • Built with Hardhat
  • Uses OpenZeppelin contracts
  • Implements EIP-712 for typed signatures
  • Inspired by minimal blockchain design patterns

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •