7.4 KiB
05 — Smart Contract: DocumentRegistry
File: blockchain/contracts/DocumentRegistry.sol
Solidity version: ^0.8.24
Network: Ethereum Sepolia Testnet (production) / Hardhat Local (development)
Purpose: Tamper-proof, timestamped proof-of-deposit for BFSI contract documents.
Design Philosophy
No document content is stored on-chain — only its SHA-256 hash.
This design provides:
- Privacy: No sensitive data ever reaches the blockchain
- Verifiability: Anyone with the original document can verify it existed at a specific time
- Immutability: Once registered, the hash and timestamp cannot be altered
- Legal Standing: Block timestamp provides a cryptographically provable submission date
How It Works
1. User uploads contract PDF to LexiChain
2. Server computes SHA-256 hash of the raw file bytes
3. Server wallet signs and sends registerDocument(bytes32) transaction
4. Transaction is mined → block.timestamp becomes the proof date
5. txHash, blockNumber, blockTimestamp saved to PostgreSQL
6. User can verify anytime via verifyDocument(bytes32)
Contract Architecture
State Variables
| Variable | Type | Visibility | Description |
|---|---|---|---|
owner |
address |
public |
Platform backend wallet address |
totalDocuments |
uint256 |
public |
Total number of registered document hashes |
documents |
mapping(bytes32 => DocumentRecord) |
private |
Hash → registration record |
depositorDocuments |
mapping(address => bytes32[]) |
private |
Address → list of hashes they registered |
Structs
struct DocumentRecord {
uint256 timestamp; // block.timestamp at registration
address depositor; // msg.sender (platform wallet)
bool exists; // false = not registered
}
Events
event DocumentRegistered(
bytes32 indexed docHash,
uint256 timestamp,
address indexed depositor
);
event DocumentVerified(
bytes32 indexed docHash,
bool exists,
address indexed verifier
);
Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
Function Reference
registerDocument(bytes32 _docHash) — write
Access: onlyOwner
Gas: ~50,000 gas
Registers a document hash on-chain. Reverts if the same hash is already registered (idempotent via requires).
Parameters:
_docHash— SHA-256 hash of the document asbytes32
Emits: DocumentRegistered(docHash, block.timestamp, msg.sender)
verifyDocument(bytes32 _docHash) — view (free)
Access: Anyone
Gas: 0 (read-only call)
Checks if a document hash is registered and returns its record.
Returns:
bool exists // true if registered
uint256 timestamp // Unix timestamp of registration (0 if not found)
address depositor // Address that registered it (address(0) if not found)
getTimestamp(bytes32 _docHash) — view (free)
Returns only the registration timestamp for a given hash (0 if not registered).
getDocumentsByDepositor(address _depositor) — view (free)
Returns all document hashes registered by a specific address.
getDocumentCount(address _depositor) — view (free)
Returns the count of documents registered by a specific address.
transferOwnership(address _newOwner) — write
Access: onlyOwner
Transfers contract ownership to a new address. Required if the platform wallet changes.
ABI (Application Binary Interface)
[
"function registerDocument(bytes32 _docHash) external",
"function verifyDocument(bytes32 _docHash) external view returns (bool exists, uint256 timestamp, address depositor)",
"function getTimestamp(bytes32 _docHash) external view returns (uint256)",
"function getDocumentsByDepositor(address _depositor) external view returns (bytes32[] memory)",
"function getDocumentCount(address _depositor) external view returns (uint256)",
"function totalDocuments() external view returns (uint256)",
"function owner() external view returns (address)",
"function transferOwnership(address _newOwner) external",
"event DocumentRegistered(bytes32 indexed docHash, uint256 timestamp, address indexed depositor)"
]
Security Model
| Property | Implementation |
|---|---|
| Owner-only writes | onlyOwner modifier prevents anyone else from registering |
| No duplicates | require(!documents[hash].exists) prevents double-registration |
| Privacy | Only hashes stored — no filename, no metadata, no user data |
| Immutability | No update/delete functions — hashes are permanent once registered |
| Dedicated wallet | Platform uses a separate wallet (not a personal wallet) |
Deployment
Prerequisites
-
Install Hardhat dependencies:
cd blockchain npm install -
Compile the contract:
npx hardhat compile
Local Development (Hardhat Node)
The npm run dev command in the root automatically:
- Starts a Hardhat node on
http://localhost:8545 - Deploys
DocumentRegistry.solto the local chain - Saves the contract address to
blockchain/.dev-deploy.json - Starts the Next.js dev server with the correct env vars
To deploy manually:
# Terminal 1: Start local node
cd blockchain
npx hardhat node
# Terminal 2: Deploy
npx hardhat run scripts/deploy.ts --network localhost
Production (Ethereum Sepolia Testnet)
Why Sepolia?
- 100% free (no real ETH needed)
- Stable Ethereum testnet maintained by the Ethereum Foundation
- Supported by Etherscan, Alchemy, Infura, MetaMask
- No node to run or maintain
- Transactions viewable on https://sepolia.etherscan.io
Step 1: Get Sepolia ETH (free)
Use one of these faucets:
- https://sepoliafaucet.com (Alchemy — requires account)
- https://faucet.quicknode.com/ethereum/sepolia
- https://www.infura.io/faucet/sepolia
A few test ETH is enough for thousands of contract registrations (~0.0005 ETH per registration).
Step 2: Set environment variables
# In blockchain/.env or your shell:
SEPOLIA_RPC_URL=https://rpc.sepolia.org
DEPLOYER_PRIVATE_KEY=0x<your-dedicated-wallet-private-key>
⚠️ CRITICAL: Never use a wallet that holds real ETH. Create a dedicated deployment wallet.
Step 3: Deploy to Sepolia
cd blockchain
npx hardhat run scripts/deploy.ts --network sepolia
The script outputs:
DocumentRegistry deployed to: 0x<CONTRACT_ADDRESS>
BLOCKCHAIN_CONTRACT_ADDRESS=0x<CONTRACT_ADDRESS>
Step 4: Set app environment variables
BLOCKCHAIN_NETWORK=sepolia
BLOCKCHAIN_RPC_URL=https://rpc.sepolia.org
BLOCKCHAIN_CONTRACT_ADDRESS=0x<your-deployed-address>
BLOCKCHAIN_PRIVATE_KEY=0x<your-wallet-private-key>
Step 5: Verify deployment (optional)
Visit https://sepolia.etherscan.io/address/<CONTRACT_ADDRESS> to confirm deployment.
Running Tests
cd blockchain
npx hardhat test
Test file: blockchain/test/DocumentRegistry.test.ts
Deployed Contract Information
After deploying, record your contract address here for team reference:
| Network | Contract Address | Deployer | Deployed At |
|---|---|---|---|
| Sepolia | 0x... |
0x... |
— |
| Hardhat (dev) | Auto-deployed | Account #0 | Per session |