Skip to content

Latest commit

 

History

History
347 lines (264 loc) · 12.4 KB

05-erc721-5.md

File metadata and controls

347 lines (264 loc) · 12.4 KB
title actions requireLogin material
ERC721: Logika Prevodov
checkAnswer
hints
true
editor
language startingCode answer
sol
zombieownership.sol zombieattack.sol zombiehelper.sol zombiefeeding.sol zombiefactory.sol ownable.sol erc721.sol
pragma solidity ^0.4.19; import "./zombieattack.sol"; import "./erc721.sol"; contract ZombieOwnership is ZombieAttack, ERC721 { function balanceOf(address _owner) public view returns (uint256 _balance) { return ownerZombieCount[_owner]; } function ownerOf(uint256 _tokenId) public view returns (address _owner) { return zombieToOwner[_tokenId]; } // Tu definuj _transfer() function transfer(address _to, uint256 _tokenId) public { } function approve(address _to, uint256 _tokenId) public { } function takeOwnership(uint256 _tokenId) public { } }
pragma solidity ^0.4.19; import "./zombiehelper.sol"; contract ZombieAttack is ZombieHelper { uint randNonce = 0; uint attackVictoryProbability = 70; function randMod(uint _modulus) internal returns(uint) { randNonce++; return uint(keccak256(now, msg.sender, randNonce)) % _modulus; } function attack(uint _zombieId, uint _targetId) external onlyOwnerOf(_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); } } }
pragma solidity ^0.4.19; 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 { owner.transfer(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) onlyOwnerOf(_zombieId) { zombies[_zombieId].name = _newName; } function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) onlyOwnerOf(_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; } }
pragma solidity ^0.4.19; 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 onlyOwnerOf(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 onlyOwnerOf(_zombieId) { Zombie storage myZombie = zombies[_zombieId]; require(_isReady(myZombie)); _targetDna = _targetDna % dnaModulus; uint newDna = (myZombie.dna + _targetDna) / 2; if (keccak256(_species) == keccak256("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"); } }
pragma solidity ^0.4.19; 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]++; NewZombie(id, _name, _dna); } function _generateRandomDna(string _str) private view returns (uint) { uint rand = uint(keccak256(_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); } }
/** * @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 public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ function Ownable() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @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 { require(newOwner != address(0)); OwnershipTransferred(owner, newOwner); owner = newOwner; } }
contract ERC721 { event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); function balanceOf(address _owner) public view returns (uint256 _balance); function ownerOf(uint256 _tokenId) public view returns (address _owner); function transfer(address _to, uint256 _tokenId) public; function approve(address _to, uint256 _tokenId) public; function takeOwnership(uint256 _tokenId) public; }
pragma solidity ^0.4.19; import "./zombieattack.sol"; import "./erc721.sol"; contract ZombieOwnership is ZombieAttack, ERC721 { function balanceOf(address _owner) public view returns (uint256 _balance) { return ownerZombieCount[_owner]; } function ownerOf(uint256 _tokenId) public view returns (address _owner) { return zombieToOwner[_tokenId]; } function _transfer(address _from, address _to, uint256 _tokenId) private { ownerZombieCount[_to]++; ownerZombieCount[_from]--; zombieToOwner[_tokenId] = _to; Transfer(_from, _to, _tokenId); } function transfer(address _to, uint256 _tokenId) public { } function approve(address _to, uint256 _tokenId) public { } function takeOwnership(uint256 _tokenId) public { } }

Paráda, podarilo sa nám vyriešiť konflikt názvov.

Teraz budeme ďalej pokračovat v našej ERC721 implementácií. Pozrieme sa na to ako prevádzať tokeny medzi adresami.

Pamätaj, že ERC721 špecifikácia má 2 rôzne spôsoby ako prevádzať tokeny.

function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
  1. Prvý spôsob je taký, že vlastník tokenu zavolá transfer funkciu s adresou address na ktorú previesť token identifikovaný ID _tokenId.

  2. Druhá možnosť je nasledovná. Vlastník tokenu najprv zavolá approve (schváliť) s rovnakým parametrami ako v prípade transfer. Tým sa token neprenesie na cudzí účet okamžite, miesto toho si ale kontrakt zapamätá, že špecifkovaná adresa má právo tento token prevziať do svojho vlastníctva. Táto informácia sa obvykle uloží do dátovej štruktúry typu mapping (uint256 => address). Keď potom niekto zavolá takeOwnership (prevziať do vlastníctva), kontrakt skontroluje, či je msg.sender oprávnený si tento token prevziať. Ak áno, token je definitívne prevedený na jeho účet.

Oba spôsoby, transfer aj takeOwnership, obstahujú rovnakú logiku na prevod vlastníctva, len v prehodenom poradí (v jednom prípade zavolá funkciu odosielateľ tokenov, v druhom prípade funkciu volá príjmateľ tokenov).

Dáva preto zmysel aby sme túto logiku vyňali do súkromnej funkcie. Nazveme ju _transfer a použijeme ju v oboch metódach. Nechceme predsa opakovať rovnaký kód dva krát.

Vyskúšaj si to sám

Poďme definovať logiku pre _transfer.

  1. Definuje funkciu pomenovanú _transfer. Tá bude príjmať 3 argumenty, address _from, address _to a uint256 _tokenId. Táto funkcia by mala byť private.

  2. Máme 2 mapovania, ktoré budeme meniť pri zmene vlastníctva. Jedno je ownerZombieCount (ktoré udržuje informáciu o počte, kto koľko zombie vlasntí) a druhé je zombieToOwner (ktoré si drží prehľad vlastníctve jednotlivých zombie).

Prvá vec, ktorú by naša funkcia mala spraviť je inkrementovať ownerZombieCount pre osobu príjmajúcu zombie. (address _to). Použi ++ na inkrementáciu.

  1. Ďalej budeme musieť dekrementovať ownerZombieCount pre osobu, ktorá odosiela zombie (address _from). Použi -- na dekrementáciu.

  2. Nakoniec aktualizujeme zombieToOwner mapovanie tak, aby bola hodnota pod kľúčom _tokenId nastavená na hodnotu _to.

  3. Klamal som, to posledný krok nebol. Zostáva nám spraviť ešte jedna vec.

ERC721 špecifikácia deklaruje Transfer udalosť. Posledný riadok našej funkcie by mal vyvolať udalosť Transfer so správnymi parametrami. Pozri si erc721.sol pre primonetie, aké parametre táto udalosť pri vyvolaní očakáva.