-
-
Save Malyugin-Anton/053ae58c7381a9a860b2785dfe3b7868 to your computer and use it in GitHub Desktop.
pragma solidity ^0.4.22; | |
import "./utils/SafeMath.sol"; | |
import "./HEROES.sol"; | |
contract Mentoring { | |
using SafeMath for uint256; | |
using SafeMath for uint32; | |
event BecomeMentor(uint256 mentorId); | |
event StartLecture(uint256 lectureId, | |
uint256 mentorId, | |
uint256 studentId, | |
uint32 levelUp, | |
uint256 levelPrice, | |
uint256 cost, | |
uint256 startedAt, | |
uint256 endsAt); | |
event BreakMentoring(uint256 mentorId); | |
event ChangeLevelPrice(uint256 mentorId, uint256 newLevelPrice); | |
event Withdraw(address to, uint256 amount); | |
struct Lecture { | |
uint256 mentorId; | |
uint256 studentId; | |
uint32 levelUp; | |
uint256 levelPrice; | |
uint256 cost; | |
uint256 startedAt; | |
uint256 endsAt; | |
} | |
address admin; | |
HEROES heroes; | |
address bank; | |
uint256 interest; | |
mapping(address => uint256) internal balances; | |
uint256 levelUpTime = 20 minutes; | |
mapping(uint256 => uint256) internal prices; | |
Lecture[] internal lectures; | |
/* tokenId => lecture index */ | |
mapping(uint256 => uint256) internal studentToLecture; | |
mapping(uint256 => uint256) internal mentorToLecture; | |
modifier onlyAdmin() { | |
require(msg.sender == admin); | |
_; | |
} | |
modifier onlyOwnerOf(uint256 _tokenId) { | |
require(heroes.ownerOf(_tokenId) == msg.sender); | |
_; | |
} | |
constructor (HEROES _heroes) public { | |
admin = msg.sender; | |
heroes = _heroes; | |
} | |
// БАНКИНГ | |
function () public payable { | |
require(bank != address(0)); | |
balances[bank] = balances[bank].add(msg.value); | |
} | |
function setBank(address _bank) external onlyAdmin { | |
require(_bank != address(0)); | |
bank = _bank; | |
} | |
/** | |
* Вывести деньги с аккаунта msg.sender | |
*/ | |
function withdraw(address _account) external { | |
require(_account != address(0)); | |
require(msg.sender == _account || msg.sender == admin); | |
uint256 balance = balances[_account]; | |
if (balance > 0) { | |
_account.transfer(balance); | |
balances[_account] = 0; | |
emit Withdraw(_account, balance); | |
} | |
} | |
/** | |
* Списать со счёта | |
*/ | |
function writeOff(address _account, uint256 _amount) | |
external | |
onlyAdmin | |
{ | |
require(balances[_account] >= _amount); | |
balances[bank] = balances[bank].add(_amount); | |
balances[_account] = balances[_account].sub(_amount); | |
} | |
/** | |
* Записать на счёт | |
*/ | |
function writeIn(address _account, uint256 _amount) | |
external | |
onlyAdmin | |
{ | |
require(balances[bank] >= _amount); | |
balances[_account] = balances[_account].add(_amount); | |
balances[bank] = balances[bank].sub(_amount); | |
} | |
/** | |
* Возвращает баланс указанного аккаунта. | |
*/ | |
function accountBalance(address _account) | |
external | |
returns (uint256) | |
{ | |
require(msg.sender == admin || msg.sender == _account); | |
return balances[_account]; | |
} | |
function _deposit(address _account, uint256 _amount) | |
internal | |
{ | |
uint256 bankInterest = _amount.div(100).mul(interest); | |
uint256 amount = _amount.sub(bankInterest); | |
balances[bank] = balances[bank].add(bankInterest); | |
balances[_account] = balances[_account].add(amount); | |
} | |
/** | |
* Устанавливает процент банка. | |
*/ | |
function setInterest(uint256 _interest) | |
external | |
onlyAdmin | |
{ | |
interest = _interest; | |
} | |
// MENTORING | |
/** | |
* Задаёт время апа одного левла | |
*/ | |
function setLevelUpTime(uint256 _newLevelUpTime) | |
external onlyAdmin | |
{ | |
levelUpTime = _newLevelUpTime; | |
} | |
/** | |
* Вернёт true если персонаж является ментором | |
*/ | |
function isMentor(uint256 _mentorId) | |
public view | |
returns (bool) | |
{ | |
uint256 lockedTo; | |
uint16 lockId; | |
(lockedTo, lockId) = heroes.getLock(_mentorId); | |
return _isMentor(lockedTo, lockId); | |
} | |
function _isMentor(uint256 _lockedTo, uint16 _lockId) | |
internal pure | |
returns (bool) | |
{ | |
return (_lockedTo == 0 && _lockId == 3); | |
} | |
/** | |
* Вернёт true если персонаж является студентом | |
*/ | |
function isStudent(uint256 _studentId) | |
public view | |
returns (bool) | |
{ | |
uint256 lockedTo; | |
uint16 lockId; | |
(lockedTo, lockId) = heroes.getLock(_studentId); | |
return _isStudent(lockedTo, lockId); | |
} | |
function _isStudent(uint256 _lockedTo, uint16 _lockId) | |
internal view | |
returns (bool) | |
{ | |
return (_lockId == 3 && now <= _lockedTo); | |
} | |
/** | |
* Вернёт true, если персонаж занят менторингом | |
*/ | |
function inMentoring(uint256 _tokenId) | |
public view | |
returns (bool) | |
{ | |
uint256 lockedTo; | |
uint16 lockId; | |
(lockedTo, lockId) = heroes.getLock(_tokenId); | |
return _inMentoring(lockedTo, lockId); | |
} | |
function _inMentoring(uint256 _lockedTo, uint16 _lockId) | |
internal view | |
returns (bool) | |
{ | |
return (_lockId == 3 | |
&& (now <= _lockedTo || 0 == _lockedTo)); | |
} | |
/** | |
* Вернёт true, если персонаж на занятии в текущий момент | |
*/ | |
function inLecture(uint256 _tokenId) | |
public view | |
returns (bool) | |
{ | |
uint256 asStudent = studentToLecture[_tokenId]; | |
uint256 asMentor = mentorToLecture[_tokenId]; | |
return ((asStudent != 0 && now <= lectures[asStudent].endsAt) | |
|| (asMentor != 0 || now <= lectures[asMentor].endsAt)); | |
} | |
/** | |
* Делает персонажа ментором, сохраняет стоимость | |
* его услуг в списке. | |
*/ | |
function becomeMentor(uint256 _mentorId, uint256 _levelPrice) | |
external onlyOwnerOf(_mentorId) | |
{ | |
require(heroes.isLocked(_mentorId) == false); | |
heroes.lock(_mentorId, 0, 3); | |
prices[_mentorId] = _levelPrice; | |
emit BecomeMentor(_mentorId); | |
emit ChangeLevelPrice(_mentorId, _levelPrice); | |
} | |
/** | |
* Меняет стоимость услуг ментора на поднятие одного уровня. | |
*/ | |
function changeLevelPrice(uint _mentorId, uint _newLevelPrice) | |
external onlyOwnerOf(_mentorId) | |
{ | |
require(isMentor(_mentorId)); | |
require(_newLevelPrice > 0); | |
prices[_mentorId] = _newLevelPrice; | |
emit ChangeLevelPrice(_mentorId, _newLevelPrice); | |
} | |
/** | |
* Останавливает менторство для персонажа-ментора. | |
*/ | |
function breakMentoring(uint _mentorId) | |
external onlyOwnerOf(_mentorId) | |
{ | |
// проверить что он ментор | |
require(inLecture(_mentorId) == false); | |
heroes.unlock(_mentorId, 3); | |
emit BreakMentoring(_mentorId); | |
} | |
/** | |
* Вернёт true, если ментор может тренировать студента. | |
*/ | |
function _raceIsSuitable(uint _mentorGenes, | |
uint _studentGenes) | |
internal pure | |
returns (bool) | |
{ | |
uint256 mentorRace = _mentorGenes & 0xFFFF; | |
uint256 studentRace = _studentGenes & 0xFFFF; | |
return (mentorRace == 1 | |
|| mentorRace == studentRace); | |
} | |
/** | |
* Вернёт уровень на который может поднять ментор | |
* конкретного студента. | |
*/ | |
function _calcLevelIncrease(uint32 _mentorLevel, | |
uint32 _studentLevel) | |
internal pure | |
returns (uint32) | |
{ | |
uint32 levelDiff = (_mentorLevel - _studentLevel); | |
return (levelDiff >> 1) + (levelDiff & 1); | |
} | |
/** | |
* Возващает полную стоимость обучения студента у | |
* конкретного ментора. | |
*/ | |
function calcCost(uint256 _mentorId, uint256 _studentId) | |
external view | |
returns (uint256) | |
{ | |
require(prices[_mentorId] != 0); | |
uint32 mentorLevel; | |
uint32 studentLevel; | |
(,,,,,,mentorLevel,,) = heroes.getCharacter(_mentorId); | |
(,,,,,,studentLevel,,) = heroes.getCharacter(_studentId); | |
return _calcCost(prices[_mentorId], | |
mentorLevel, | |
studentLevel); | |
} | |
function _calcCost(uint256 _levelPrice, | |
uint32 _mentorLevel, | |
uint32 _studentLevel) | |
internal pure | |
returns (uint256) | |
{ | |
require(1 <= (_mentorLevel - _studentLevel)); | |
uint32 levelIncrease = | |
_calcLevelIncrease(_mentorLevel, _studentLevel); | |
return levelIncrease.mul(_levelPrice); | |
} | |
function _calcEndsAt(uint256 _startsAt, | |
uint32 _mentorLevel, | |
uint32 _studentLevel) | |
internal view | |
returns (uint256) | |
{ | |
uint32 levelUp = | |
_calcLevelIncrease(_mentorLevel, _studentLevel); | |
return _startsAt + (levelUp * levelUpTime); | |
} | |
/** | |
* Запускает процесс обучения для указанного студента | |
* и ментора. | |
*/ | |
function startLecture(uint _mentorId, uint _studentId) | |
external payable onlyOwnerOf(_studentId) | |
{ | |
require(false == inLecture(_mentorId)); | |
require(false == inLecture(_studentId)); | |
require(true == isMentor(_mentorId)); | |
require(0 != prices[_mentorId]); | |
uint256 mentorGenes; | |
uint32 mentorLevel; | |
uint256 studentGenes; | |
uint32 studentLevel; | |
(mentorGenes,,,,,,mentorLevel,,) = | |
heroes.getCharacter(_mentorId); | |
(studentGenes,,,,,,studentLevel,,) = | |
heroes.getCharacter(_studentId); | |
// Check race | |
require(_raceIsSuitable(mentorGenes, studentGenes)); | |
// Конструируем структуру занятия | |
Lecture memory lecture = Lecture({ | |
mentorId: _mentorId, | |
studentId: _studentId, | |
levelUp: _calcLevelIncrease(mentorLevel, | |
studentLevel), | |
levelPrice: prices[_mentorId], | |
cost: _calcCost(prices[_mentorId], | |
mentorLevel, | |
studentLevel), | |
startedAt: now, | |
endsAt: _calcEndsAt(now, mentorLevel, studentLevel) | |
}); | |
uint256 lectureId = lectures.push(lecture); | |
studentToLecture[_studentId] = lectureId; | |
mentorToLecture[_mentorId] = lectureId; | |
for (uint32 i = 0; i < lecture.levelUp; i++) { | |
heroes.addWin(_studentId); | |
} | |
_deposit(heroes.ownerOf(_mentorId), msg.value); | |
emit StartLecture(lectureId, | |
_mentorId, | |
_studentId, | |
lecture.levelUp, | |
lecture.levelPrice, | |
lecture.cost, | |
lecture.startedAt, | |
lecture.endsAt); | |
} | |
/** | |
* Проверяет, что занятие существует | |
*/ | |
function lectureIsExists(uint256 _lectureId) | |
public view | |
returns (bool) | |
{ | |
return (_lectureId < lectures.length); | |
} | |
/** | |
* Вернёт занятие по ID | |
*/ | |
function getLecture(uint256 _lectureId) | |
external view | |
returns (uint256 mentorId, | |
uint256 studentId, | |
uint32 levelUp, | |
uint256 levelPrice, | |
uint256 cost, | |
uint256 startedAt, | |
uint256 endsAt) | |
{ | |
require(lectureIsExists(_lectureId)); | |
mentorId = lectures[_lectureId].mentorId; | |
studentId = lectures[_lectureId].studentId; | |
levelUp = lectures[_lectureId].levelUp; | |
levelPrice = lectures[_lectureId].levelPrice; | |
cost = lectures[_lectureId].cost; | |
startedAt = lectures[_lectureId].startedAt; | |
endsAt = lectures[_lectureId].endsAt; | |
} | |
/** | |
* Возвращает текущее занятие по токену. | |
*/ | |
function getCurrentLecture(uint _tokenId) | |
external view | |
returns (uint256 lectureId, | |
uint256 mentorId, | |
uint256 studentId, | |
uint32 levelUp, | |
uint256 levelPrice, | |
uint256 cost, | |
uint256 startedAt, | |
uint256 endsAt) | |
{ | |
uint256 asStudent = studentToLecture[_tokenId]; | |
uint256 asMentor = mentorToLecture[_tokenId]; | |
uint256 lastLectureId = asStudent > asMentor ? | |
asStudent : asMentor; | |
require(lectures[lastLectureId].endsAt > now); | |
lectureId = lastLectureId; | |
mentorId = lectures[lastLectureId].mentorId; | |
studentId = lectures[lastLectureId].studentId; | |
levelUp = lectures[lastLectureId].levelUp; | |
levelPrice = lectures[lastLectureId].levelPrice; | |
cost = lectures[lastLectureId].cost; | |
startedAt = lectures[lastLectureId].startedAt; | |
endsAt = lectures[lastLectureId].endsAt; | |
} | |
/** | |
* Вернёт параметры текущего занятия | |
*/ | |
function getCurrentLecture(uint _mentorId, uint _studentId) | |
external view | |
returns (uint256 levelUp, | |
uint256 levelPrice, | |
uint256 cost, | |
uint256 startedAt, | |
uint256 endsAt) | |
{ | |
uint256 asStudent = studentToLecture[_studentId]; | |
uint256 asMentor = mentorToLecture[_mentorId]; | |
require(asMentor != 0 && asStudent !=0); | |
require(asMentor == asStudent); | |
Lecture memory lecture = lectures[asMentor]; | |
require(now <= lecture.endsAt); | |
levelUp = lecture.levelUp; | |
levelPrice = lecture.levelPrice; | |
cost = lecture.cost; | |
startedAt = lecture.startedAt; | |
endsAt = lecture.endsAt; | |
} | |
} |
В файле контракта Mentoring.sol возникли ошибки в методах breakMentoring, becomeMentor
Менторство нужно для повышения уровня персонажей
Есть студен и есть ментор
becomeMentor - вызываем если пользователь хочет обучать студенов. Он может указать стоимость обучения за 1 level
startLecture - вызывается когда студент нашел подходящего ментора для обучения
breakMentoring - пользователь перестают быть ментором
changeLevelPrice - меняем цену за 1 level
withdraw - вывод денег из игры
В коде контракта достаточно коментов, поэтому я думаю вам будет легко ориентироваться в нем
Нужно выпустить персонажей с помощью HEROES::mintTo
Нужны только токен и менторство в данный момент.
Выгружать в VM можно только их.
В миграции менторства нужно в ручную прописать адрес перед её выгрузкой.
migrate -f 1 —to 2
// Тут прописываем адрес
migrate -f 5 —to 5
Примерно так будет выгрузка выглядеть.
Шаги могут быть такие:
- Выгрузить два контракта
- Выпустить в токене HEROES персонажа (HEROES::mintTo)
- Вызвать для него глючные функции из Mentoring про которые Антон написал
Увидеть ошибку и постараться с ней разобраться
Методы breakMentoring, becomeMentor не работают