SIP-5: Implement, adopt and enforce Samurai Levels

Motivation

This proposal suggests for the protocol to adopt and enforce a set of smart contracts (specified below) that enable, facilitate and provide access to a proposed feature of the Samurai protocol, called “Samurai Levels”

The “Samurai Levels” feature would be enabled by the protocol adopting a set of smart contracts focusing on the facilitation and provision of a gamified education/learning experience, exclusively to Samurai Node NFT holders.

Samurai Levels, with the use of the proposed set of smart contracts, would aim to provide an incentivised way of building habits and encourage individuals to learn and establish a deeper understanding of web3-related concepts and knowledge, through gamification of the learning experience and the Samurai protocol.

The implementation of this proposal shall further bolster the utility of the Samurai Node NFTs but also educate and explain fundamental concepts from the web3 industry, in a meaningful and gamified manner with the use of features and tech at the disposal of the Samurai protocol.

“Samurai Levels” would represent a Samurai Node NFT holder exclusive feature that gamifies, incentivises and rewards accurate understanding and knowledge of web3-related concepts with xHNR governance tokens.

3 000 000 xHNR governance tokens shall be minted for the purposes of facilitating the proposed Samurai Levels feature.

Specification

Samurai Levels is a smart contract aimed at providing an incentivised way of building habits and encouraging learning along the way. The core feature of this smart contract is available only to Samurai Node NFT holders. Hence, to participate, users must own at least one NFT.

The contract keeps track of two primary objectives for each wallet: Experience and Streaks.

When a user completes the daily questions on levels.samurai.financial, the levelUp function will become immediately available. Depending on the number of questions which the user answered correctly, the reward rate and experience will be calculated based on the number of correct answers and the current streak.

How to earn the streak multiplier?

Each user can earn the streak multiplier, by participating in the daily learning objectives once in every 24 hours cycle. Each user technically starts with a multiplier of 0. By completing your first 20 questions, you will initialise your wallet with multiplier 1. If the next task is completed between the following range of time:

24 hours since last completion < current time < 48 hours since last completion

Then the user’s streak will increase by 1 until the streak cap of 20 is reached. The streak of 20 can be maintained by completing questions daily until the streak is broken by the boundary limits stated above.

How are rewards and experience calculated?

The reward is calculated using the following formula:

Reward = Reward Rate * Number of Correct Answers * (Current Streak / 2)

The experience is calculated using the following formula:

Experience = Number of Correct Answers * 50 * Current Streak

The proposed Solidity code / smart contracts

SamuraiLevels.sol

// SPDX-License-Identifier: MIT pragma solidity 0.8.13;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/interfaces/IERC721.sol"; import "@openzeppelin/contracts/interfaces/IERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/security/Pausable.sol";

contract SamuraiLevels is Ownable, Pausable, ReentrancyGuard { struct UserInfo { uint256 experience; uint256 lastClaimed; uint256 streak; } mapping(address => UserInfo) public users; uint256 public rewardRate; uint256 public streakCap;

IERC20 public xhnr; IERC721 public hnrNodes;

using SafeMath for uint256;

constructor( address _xhnr, address _hnrNodes, uint256 _rewardRate, uint256 _streakCap ) { xhnr = IERC20(_xhnr); hnrNodes = IERC721(_hnrNodes); rewardRate = _rewardRate; streakCap = _streakCap; }

function levelUp(uint256 _data) external whenNotPaused nonReentrant { require(_data <= 20, "Contract: too many questions!");

address sender = msg.sender;
require(hnrNodes.balanceOf(sender) >= 1, "Contract: not a holder!");

UserInfo storage user = users[sender];

uint256 blockTimeNow = block.timestamp;
uint256 oneDay = 24 * 60 * 60;
uint256 twoDays = oneDay * 2;
uint256 timeDiff = blockTimeNow - user.lastClaimed;

require(timeDiff >= oneDay, "Contract: not refreshed yet!");

if (user.streak == 0) {
  user.streak = 1;
} else {
  if (timeDiff <= twoDays && (user.streak + 1 <= streakCap)) {
    user.streak += 1;
  } else if (timeDiff > twoDays && user.streak > 1) {
    user.streak = 1;
  }
}

// 50 is the xp gain rate
uint256 newXp = _data * 50 * user.streak;
// multiply before divide
uint256 reward = (rewardRate * _data * user.streak) / 2;

user.experience += newXp;
user.lastClaimed = blockTimeNow;

xhnr.transfer(sender, reward);

}

function getUserInfo() external view returns ( uint256, uint256, uint256 ) { UserInfo memory user = users[msg.sender]; return (user.experience, user.lastClaimed, user.streak); }

function setRewardRate(uint256 _rewardRate) external onlyOwner { rewardRate = _rewardRate; }

function setStreakCap(uint256 _streakCap) external onlyOwner { streakCap = _streakCap; }

function pause(bool en) external onlyOwner { en ? _pause() : _unpause(); } }

Last updated