ScoreChain is a blockchain-based smart contract built using Solidity that enables secure, transparent, and tamper-proof publication of student exam results.
Unlike traditional centralized result systems, ScoreChain leverages blockchain immutability to ensure that once a result is published, it can never be altered or deleted.
This helps educational institutions build trust and transparency, while allowing students to verify their scores publicly without revealing personal information.
- The exam authority (owner) deploys the smart contract and becomes the only entity authorized to publish results.
- Each student is identified using a hashed ID (keccak256), preserving privacy while allowing verification.
- Results once published are permanently stored on-chain and cannot be modified or tampered with.
- Anyone can verify a result using the student's hash (computed from student ID and a private salt).
✅ Owner-only publishing – Only the contract owner (exam authority) can publish results.
✅ Immutable records – Once added, results cannot be changed or deleted.
✅ Privacy-preserving – Uses keccak256 hashing to protect student identities.
✅ Transparent verification – Anyone can read results on-chain using the student hash.
✅ Gas-efficient – Designed with minimal storage and simple mappings.
✅ Ownership transfer – Secure ownership transfer capability for administrative flexibility.
Network: Celo Sepolia Testnet
Explorer: View on Blockscout
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title ScoreChain - Tamper-proof Exam Result Publication System
/// @author
/// @notice Owner can publish exam results that cannot be modified once published.
/// @dev Results are stored using a hashed student identifier for privacy.
contract ScoreChain {
/// @notice Address of the contract owner (e.g., the exam authority)
address public owner;
/// @notice Emitted when ownership is transferred
/// @param previousOwner The address of the previous owner
/// @param newOwner The address of the new owner
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice Emitted when a new exam result is published
/// @param studentHash The hashed identifier of the student
/// @param score The numeric score of the student
/// @param grade The grade or remark (e.g., "A+", "Pass")
/// @param timestamp The block timestamp when published
/// @param examId The exam identifier (e.g., "Math2025-01")
event ResultPublished(bytes32 indexed studentHash, uint16 score, string grade, uint256 timestamp, string examId);
/// @notice Struct to represent a student's result
struct Result {
uint16 score; // numeric score (0–65535)
string grade; // e.g. "A", "B+", "Pass"
string examId; // exam identifier (e.g. "Math2025-01")
uint256 timestamp; // block timestamp when published
bool exists; // true if record already exists
}
/// @notice Mapping from student hash to their exam result
mapping(bytes32 => Result) private results;
/// @notice Restricts functions to only the contract owner
modifier onlyOwner() {
require(msg.sender == owner, "ScoreChain: caller is not the owner");
_;
}
/// @notice Initializes the contract setting the deployer as the initial owner
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers contract ownership to another address
/// @param newOwner The address of the new owner
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "ScoreChain: new owner is zero address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
/// @notice Publishes a result for a student (identified by studentHash). Cannot overwrite existing result.
/// @param studentHash keccak256 hash of the student's identifier (computed off-chain)
/// @param score Numeric score (0–65535)
/// @param grade Human-readable grade (e.g., "A")
/// @param examId Exam identifier string (e.g., "Math2025-01")
function publishResult(
bytes32 studentHash,
uint16 score,
string calldata grade,
string calldata examId
) external onlyOwner {
require(!results[studentHash].exists, "ScoreChain: result already published for this student");
results[studentHash] = Result({
score: score,
grade: grade,
examId: examId,
timestamp: block.timestamp,
exists: true
});
emit ResultPublished(studentHash, score, grade, block.timestamp, examId);
}
/// @notice Reads a published result using the student's hash
/// @param studentHash keccak256 hash used when publishing
/// @return score The numeric score of the student
/// @return grade The letter or descriptive grade
/// @return examId The exam identifier string
/// @return timestamp The block timestamp when the result was published
/// @return exists Boolean indicating whether the result exists
function getResult(bytes32 studentHash)
external
view
returns (
uint16 score,
string memory grade,
string memory examId,
uint256 timestamp,
bool exists
)
{
Result storage r = results[studentHash];
return (r.score, r.grade, r.examId, r.timestamp, r.exists);
}
/// @notice Computes a student's hash (for reference)
/// @dev It’s safer to compute this off-chain so the salt remains secret
/// @param studentId Public student identifier (e.g., roll number)
/// @param salt Secret salt string known only to admin and student
/// @return hash The keccak256 hash combining studentId and salt
function computeStudentHash(string calldata studentId, string calldata salt)
external
pure
returns (bytes32 hash)
{
hash = keccak256(abi.encodePacked(studentId, "|", salt));
}
}- Open Remix IDE
- Create a new file named
ScoreChain.sol - Paste the above Solidity code
- Compile using compiler version 0.8.19
- Deploy the contract using Celo Sepolia Testnet
Step 1: Compute Student Hash (Off-chain) Use JavaScript (with ethers.js):
ethers.utils.keccak256(ethers.utils.toUtf8Bytes("ROLL123|secret123"));Step 2: Publish Result (Owner only)
publishResult("0x4f9a7a2dbd80f...", 95, "A+", "Math2025-01");Step 3: Retrieve Result (Anyone)
getResult("0x4f9a7a2dbd80f...");- Solidity (0.8.19) – Smart contract language
- Celo Blockchain (Testnet) – Decentralized and eco-friendly network
- Remix IDE – For writing and deploying contracts
- Blockscout – To explore and verify contracts on-chain
This project is licensed under the MIT License. You are free to use, modify, and distribute it with attribution.
⭐ If you find this project useful, give it a star on GitHub!