forked from CryptozombiesHQ/cryptozombie-lessons
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
367 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,367 @@ | ||
--- | ||
title: balanceOf & ownerOf | ||
actions: ['checkAnswer', 'hints'] | ||
requireLogin: true | ||
material: | ||
editor: | ||
language: sol | ||
startingCode: | ||
"zombieownership.sol": | | ||
pragma solidity ^0.4.25; | ||
import "./zombieattack.sol"; | ||
import "./erc721.sol"; | ||
contract ZombieOwnership is ZombieAttack, ERC721 { | ||
function balanceOf(address _owner) external view returns (uint256) { | ||
// 1. Тут вернуть количество зомби которое есть у `_owner` | ||
} | ||
function ownerOf(uint256 _tokenId) external view returns (address) { | ||
// 2. Тут вернуть владельца `_tokenId` | ||
} | ||
function transferFrom(address _from, address _to, uint256 _tokenId) external payable { | ||
} | ||
function approve(address _approved, uint256 _tokenId) external payable { | ||
} | ||
} | ||
"zombieattack.sol": | | ||
pragma solidity ^0.4.25; | ||
import "./zombiehelper.sol"; | ||
contract ZombieAttack is ZombieHelper { | ||
uint randNonce = 0; | ||
uint attackVictoryProbability = 70; | ||
function randMod(uint _modulus) internal returns(uint) { | ||
randNonce++; | ||
return uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % _modulus; | ||
} | ||
function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) { | ||
Zombie storage myZombie = zombies[_zombieId]; | ||
Zombie storage enemyZombie = zombies[_targetId]; | ||
uint rand = randMod(100); | ||
if (rand <= attackVictoryProbability) { | ||
myZombie.winCount++; | ||
myZombie.level++; | ||
enemyZombie.lossCount++; | ||
feedAndMultiply(_zombieId, enemyZombie.dna, "zombie"); | ||
} else { | ||
myZombie.lossCount++; | ||
enemyZombie.winCount++; | ||
_triggerCooldown(myZombie); | ||
} | ||
} | ||
} | ||
"zombiehelper.sol": | | ||
pragma solidity ^0.4.25; | ||
import "./zombiefeeding.sol"; | ||
contract ZombieHelper is ZombieFeeding { | ||
uint levelUpFee = 0.001 ether; | ||
modifier aboveLevel(uint _level, uint _zombieId) { | ||
require(zombies[_zombieId].level >= _level); | ||
_; | ||
} | ||
function withdraw() external onlyOwner { | ||
address _owner = owner(); | ||
_owner.transfer(address(this).balance); | ||
} | ||
function setLevelUpFee(uint _fee) external onlyOwner { | ||
levelUpFee = _fee; | ||
} | ||
function levelUp(uint _zombieId) external payable { | ||
require(msg.value == levelUpFee); | ||
zombies[_zombieId].level++; | ||
} | ||
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) { | ||
zombies[_zombieId].name = _newName; | ||
} | ||
function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) { | ||
zombies[_zombieId].dna = _newDna; | ||
} | ||
function getZombiesByOwner(address _owner) external view returns(uint[]) { | ||
uint[] memory result = new uint[](ownerZombieCount[_owner]); | ||
uint counter = 0; | ||
for (uint i = 0; i < zombies.length; i++) { | ||
if (zombieToOwner[i] == _owner) { | ||
result[counter] = i; | ||
counter++; | ||
} | ||
} | ||
return result; | ||
} | ||
} | ||
"zombiefeeding.sol": | | ||
pragma solidity ^0.4.25; | ||
import "./zombiefactory.sol"; | ||
contract KittyInterface { | ||
function getKitty(uint256 _id) external view returns ( | ||
bool isGestating, | ||
bool isReady, | ||
uint256 cooldownIndex, | ||
uint256 nextActionAt, | ||
uint256 siringWithId, | ||
uint256 birthTime, | ||
uint256 matronId, | ||
uint256 sireId, | ||
uint256 generation, | ||
uint256 genes | ||
); | ||
} | ||
contract ZombieFeeding is ZombieFactory { | ||
KittyInterface kittyContract; | ||
modifier ownerOf(uint _zombieId) { | ||
require(msg.sender == zombieToOwner[_zombieId]); | ||
_; | ||
} | ||
function setKittyContractAddress(address _address) external onlyOwner { | ||
kittyContract = KittyInterface(_address); | ||
} | ||
function _triggerCooldown(Zombie storage _zombie) internal { | ||
_zombie.readyTime = uint32(now + cooldownTime); | ||
} | ||
function _isReady(Zombie storage _zombie) internal view returns (bool) { | ||
return (_zombie.readyTime <= now); | ||
} | ||
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) { | ||
Zombie storage myZombie = zombies[_zombieId]; | ||
require(_isReady(myZombie)); | ||
_targetDna = _targetDna % dnaModulus; | ||
uint newDna = (myZombie.dna + _targetDna) / 2; | ||
if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) { | ||
newDna = newDna - newDna % 100 + 99; | ||
} | ||
_createZombie("NoName", newDna); | ||
_triggerCooldown(myZombie); | ||
} | ||
function feedOnKitty(uint _zombieId, uint _kittyId) public { | ||
uint kittyDna; | ||
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId); | ||
feedAndMultiply(_zombieId, kittyDna, "kitty"); | ||
} | ||
} | ||
"zombiefactory.sol": | | ||
pragma solidity ^0.4.25; | ||
import "./ownable.sol"; | ||
contract ZombieFactory is Ownable { | ||
event NewZombie(uint zombieId, string name, uint dna); | ||
uint dnaDigits = 16; | ||
uint dnaModulus = 10 ** dnaDigits; | ||
uint cooldownTime = 1 days; | ||
struct Zombie { | ||
string name; | ||
uint dna; | ||
uint32 level; | ||
uint32 readyTime; | ||
uint16 winCount; | ||
uint16 lossCount; | ||
} | ||
Zombie[] public zombies; | ||
mapping (uint => address) public zombieToOwner; | ||
mapping (address => uint) ownerZombieCount; | ||
function _createZombie(string _name, uint _dna) internal { | ||
uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1; | ||
zombieToOwner[id] = msg.sender; | ||
ownerZombieCount[msg.sender]++; | ||
emit NewZombie(id, _name, _dna); | ||
} | ||
function _generateRandomDna(string _str) private view returns (uint) { | ||
uint rand = uint(keccak256(abi.encodePacked(_str))); | ||
return rand % dnaModulus; | ||
} | ||
function createRandomZombie(string _name) public { | ||
require(ownerZombieCount[msg.sender] == 0); | ||
uint randDna = _generateRandomDna(_name); | ||
randDna = randDna - randDna % 100; | ||
_createZombie(_name, randDna); | ||
} | ||
} | ||
"ownable.sol": | | ||
pragma solidity ^0.4.25; | ||
/** | ||
* @title Ownable | ||
* @dev The Ownable contract has an owner address, and provides basic authorization control | ||
* functions, this simplifies the implementation of "user permissions". | ||
*/ | ||
contract Ownable { | ||
address private _owner; | ||
event OwnershipTransferred( | ||
address indexed previousOwner, | ||
address indexed newOwner | ||
); | ||
/** | ||
* @dev The Ownable constructor sets the original `owner` of the contract to the sender | ||
* account. | ||
*/ | ||
constructor() internal { | ||
_owner = msg.sender; | ||
emit OwnershipTransferred(address(0), _owner); | ||
} | ||
/** | ||
* @return the address of the owner. | ||
*/ | ||
function owner() public view returns(address) { | ||
return _owner; | ||
} | ||
/** | ||
* @dev Throws if called by any account other than the owner. | ||
*/ | ||
modifier onlyOwner() { | ||
require(isOwner()); | ||
_; | ||
} | ||
/** | ||
* @return true if `msg.sender` is the owner of the contract. | ||
*/ | ||
function isOwner() public view returns(bool) { | ||
return msg.sender == _owner; | ||
} | ||
/** | ||
* @dev Allows the current owner to relinquish control of the contract. | ||
* @notice Renouncing to ownership will leave the contract without an owner. | ||
* It will not be possible to call the functions with the `onlyOwner` | ||
* modifier anymore. | ||
*/ | ||
function renounceOwnership() public onlyOwner { | ||
emit OwnershipTransferred(_owner, address(0)); | ||
_owner = address(0); | ||
} | ||
/** | ||
* @dev Allows the current owner to transfer control of the contract to a newOwner. | ||
* @param newOwner The address to transfer ownership to. | ||
*/ | ||
function transferOwnership(address newOwner) public onlyOwner { | ||
_transferOwnership(newOwner); | ||
} | ||
/** | ||
* @dev Transfers control of the contract to a newOwner. | ||
* @param newOwner The address to transfer ownership to. | ||
*/ | ||
function _transferOwnership(address newOwner) internal { | ||
require(newOwner != address(0)); | ||
emit OwnershipTransferred(_owner, newOwner); | ||
_owner = newOwner; | ||
} | ||
} | ||
"erc721.sol": | | ||
pragma solidity ^0.4.25; | ||
contract ERC721 { | ||
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); | ||
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); | ||
function balanceOf(address _owner) external view returns (uint256); | ||
function ownerOf(uint256 _tokenId) external view returns (address); | ||
function transferFrom(address _from, address _to, uint256 _tokenId) external payable; | ||
function approve(address _approved, uint256 _tokenId) external payable; | ||
} | ||
answer: | | ||
pragma solidity ^0.4.25; | ||
import "./zombieattack.sol"; | ||
import "./erc721.sol"; | ||
contract ZombieOwnership is ZombieAttack, ERC721 { | ||
function balanceOf(address _owner) external view returns (uint256) { | ||
return ownerZombieCount[_owner]; | ||
} | ||
function ownerOf(uint256 _tokenId) external view returns (address) { | ||
return zombieToOwner[_tokenId]; | ||
} | ||
function transferFrom(address _from, address _to, uint256 _tokenId) external payable { | ||
} | ||
function approve(address _approved, uint256 _tokenId) external payable { | ||
} | ||
} | ||
--- | ||
|
||
Отлично, давайте углубимся в реализацию ERC721! | ||
|
||
Мы забежали вперед и скопировали пустую оболочку всех функций, которые вы будете реализовывать на этом уроке. | ||
|
||
В этой главе мы собираемся реализовать первые два метода: `balanceOf` и `ownerOf`. | ||
|
||
### `balanceOf` | ||
|
||
``` | ||
function balanceOf(address _owner) external view returns (uint256 _balance); | ||
``` | ||
Эта функция просто принимает адрес и возвращает количество токенов, принадлежащих этому адресу. | ||
|
||
В нашем случае наши "токены" - это зомби. Вы помните, где в нашем DApp мы сохранили сколько зомби у владельца? | ||
|
||
### `ownerOf` | ||
|
||
``` | ||
function ownerOf(uint256 _tokenId) external view returns (address _owner); | ||
``` | ||
Эта функция берет идентификатор токена (в нашем случае, идентификатор зомби) и возвращает адрес владельца. | ||
|
||
Опять же, это очень просто для нас реализовать, так как у нас уже есть `mapping` в нашем DApp, которое хранит эту информацию. Мы можем реализовать эту функцию в одну строку, просто с помощью оператора `return`. | ||
|
||
> Примечание: Помните, что `uint256` эквивалентно `uint`. Мы использовали `uint` в нашем коде до сих пор, но здесь мы используем `uint256` потому что мы копируем из спецификации. | ||
## Проверь себя | ||
|
||
Я оставлю это Вам, чтобы выяснить, как реализовать эти 2 функции. | ||
|
||
Каждая функция должна просто состоять из 1 строки кода и оператора `return`. Взгляните на наш код из предыдущих уроков, чтобы вспомить, где мы храним эти данные. Если вы не можете это выяснить, нажмите кнопку "показать ответ" для помощи. | ||
|
||
1. Реализовать `balanceOf`, чтобы вернуть количество зомби которое есть у `_owner`. | ||
|
||
2. Реализовать `ownerOf`, чтобы вернуть адрес того, кто владеет зомби с идентификатором `_tokenId`. |