Play-to-Earn Reward Engine

What it does:
Automatically distributes rewards (tokens, NFTs, or in-game currency) to players based on achievements, milestones, or gameplay events on-chain.

Why it matters:
Ensures transparent, tamper-proof reward distribution, incentivizes player engagement, and allows developers to implement programmable reward structures.

How it works:

  • Players register or connect wallets to the reward engine

  • Smart contracts track in-game achievements, milestones, or events

  • Rewards are calculated automatically based on predefined rules (time played, challenges completed, or leaderboard rank)

  • Rewards are distributed in tokens, NFTs, or other on-chain assets

  • Integrates with Gaming Asset Ownership, Creator Collaboration Revenue Split, or NFT Marketplaces

  • Dashboards visualize earned rewards, pending payouts, and achievement history

  • Supports multi-game or cross-platform reward tracking

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title PlayToEarnRewardEngine
 * @author Nam
 * @notice Distributes on-chain P2E rewards based on achievements and milestones
 */
contract PlayToEarnRewardEngine is Ownable {

    IERC20 public rewardToken;

    struct Player {
        uint256 totalRewards;
        mapping(string => bool) achievements;
    }

    mapping(address => Player) public players;

    mapping(address => bool) public authorizedValidators;

    // -------------------- EVENTS --------------------

    event ValidatorApproved(address validator);
    event ValidatorRevoked(address validator);
    event AchievementUnlocked(address player, string achievementId, uint256 rewardAmount);
    event RewardClaimed(address player, uint256 amount);

    // -------------------- VALIDATOR MANAGEMENT --------------------

    function approveValidator(address _validator) external onlyOwner {
        authorizedValidators[_validator] = true;
        emit ValidatorApproved(_validator);
    }

    function revokeValidator(address _validator) external onlyOwner {
        authorizedValidators[_validator] = false;
        emit ValidatorRevoked(_validator);
    }

    modifier onlyValidator() {
        require(authorizedValidators[msg.sender], "Not authorized validator");
        _;
    }

    // -------------------- CONSTRUCTOR --------------------

    constructor(address _rewardToken) {
        rewardToken = IERC20(_rewardToken);
    }

    // -------------------- ACHIEVEMENT & REWARD MANAGEMENT --------------------

    function unlockAchievement(address _player, string calldata _achievementId, uint256 _rewardAmount) external onlyValidator {
        Player storage p = players[_player];
        require(!p.achievements[_achievementId], "Achievement already unlocked");

        p.achievements[_achievementId] = true;
        p.totalRewards += _rewardAmount;

        emit AchievementUnlocked(_player, _achievementId, _rewardAmount);
    }

    function claimRewards() external {
        Player storage p = players[msg.sender];
        uint256 amount = p.totalRewards;
        require(amount > 0, "No rewards to claim");

        p.totalRewards = 0;
        rewardToken.transfer(msg.sender, amount);

        emit RewardClaimed(msg.sender, amount);
    }

    // -------------------- VIEW FUNCTIONS --------------------

    function hasAchievement(address _player, string calldata _achievementId) external view returns (bool) {
        return players[_player].achievements[_achievementId];
    }

    function pendingRewards(address _player) external view returns (uint256) {
        return players[_player].totalRewards;
    }
}