Learn-to-Earn Smart Contract
What it does:
Rewards learners with tokens or funds for completing educational tasks, courses, or achieving skill milestones on-chain.
Why it matters:
Incentivizes learning through financial rewards, verifies completion transparently, and creates a verifiable skill-based economy.
How it works:
-
Learners enroll in courses or learning modules
-
Educators define learning milestones and associated rewards
-
Completion is verified on-chain via proof-of-completion or educator approval
-
Smart contract releases token/fund rewards automatically upon milestone completion
-
Learners can claim accumulated rewards
-
All activities, completions, and reward distributions are auditable
-
Incomplete or disputed tasks are handled per predefined rules
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title LearnToEarn
* @author Nam
* @notice Reward learners for completing tasks or courses on-chain
*/
contract LearnToEarn {
// -------------------- ROLES --------------------
address public admin;
// -------------------- STRUCTS --------------------
struct Course {
string metadataHash; // IPFS course info
uint256 rewardAmount; // wei per milestone
address educator;
bool active;
}
struct Enrollment {
uint256 courseId;
address learner;
uint256 completedMilestones;
uint256 totalRewardClaimed;
}
uint256 public courseCount;
uint256 public enrollmentCount;
mapping(uint256 => Course) public courses;
mapping(uint256 => Enrollment) public enrollments;
// -------------------- EVENTS --------------------
event CourseCreated(uint256 indexed courseId, address indexed educator);
event LearnerEnrolled(uint256 indexed enrollmentId, uint256 courseId, address learner);
event MilestoneCompleted(uint256 indexed enrollmentId, uint256 rewardAmount);
event RewardClaimed(uint256 indexed enrollmentId, address learner, uint256 amount);
// -------------------- MODIFIERS --------------------
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
modifier onlyEducator(uint256 _courseId) {
require(courses[_courseId].educator == msg.sender, "Not course educator");
_;
}
// -------------------- CONSTRUCTOR --------------------
constructor() {
admin = msg.sender;
}
// -------------------- COURSE MANAGEMENT --------------------
function createCourse(string calldata _metadataHash, uint256 _rewardAmount) external {
require(_rewardAmount > 0, "Invalid reward");
courseCount += 1;
courses[courseCount] = Course({
metadataHash: _metadataHash,
rewardAmount: _rewardAmount,
educator: msg.sender,
active: true
});
emit CourseCreated(courseCount, msg.sender);
}
// -------------------- ENROLLMENT --------------------
function enroll(uint256 _courseId) external {
require(courses[_courseId].active, "Course inactive");
enrollmentCount += 1;
enrollments[enrollmentCount] = Enrollment({
courseId: _courseId,
learner: msg.sender,
completedMilestones: 0,
totalRewardClaimed: 0
});
emit LearnerEnrolled(enrollmentCount, _courseId, msg.sender);
}
// -------------------- MILESTONE COMPLETION --------------------
function completeMilestone(uint256 _enrollmentId) external {
Enrollment storage e = enrollments[_enrollmentId];
require(msg.sender == e.learner, "Not enrolled learner");
e.completedMilestones += 1;
emit MilestoneCompleted(_enrollmentId, courses[e.courseId].rewardAmount);
}
// -------------------- REWARD CLAIM --------------------
function claimReward(uint256 _enrollmentId) external {
Enrollment storage e = enrollments[_enrollmentId];
require(msg.sender == e.learner, "Not learner");
uint256 claimable = e.completedMilestones * courses[e.courseId].rewardAmount - e.totalRewardClaimed;
require(claimable > 0, "No reward to claim");
require(address(this).balance >= claimable, "Insufficient balance");
e.totalRewardClaimed += claimable;
payable(msg.sender).transfer(claimable);
emit RewardClaimed(_enrollmentId, msg.sender, claimable);
}
// -------------------- FUNDING --------------------
receive() external payable {}
}