-
-
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 не работают