Skip to content

Latest commit

 

History

History
998 lines (893 loc) · 31.1 KB

03.md

File metadata and controls

998 lines (893 loc) · 31.1 KB
title actions requireLogin material
Comunicare con i Contratti
checkAnswer
hints
true
editor
language startingCode answer
html
index.html cryptozombies_abi.js zombieownership.sol zombieattack.sol zombiehelper.sol zombiefeeding.sol zombiefactory.sol ownable.sol safemath.sol erc721.sol
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CryptoZombies front-end</title> <script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script language="javascript" type="text/javascript" src="web3.min.js"></script> <!-- 1. Includi qui cryptozombies_abi.js --> </head> <body> <script> // 2. Inizia il codice qui window.addEventListener('load', function() { // Verifica se Web3 è stato iniettato dal browser (Mist/MetaMask) if (typeof web3 !== 'undefined') { // Usa il provider Mist/MetaMask web3js = new Web3(web3.currentProvider); } else { // Gestire il caso in cui l'utente non ha installato Metamask // Probabilmente mostrare loro un messaggio che richiede di installare Metamask } // Ora puoi avviare la tua app e accedere a web3 liberamente: startApp() }) </script> </body> </html>
var cryptoZombiesABI = [ { "constant": false, "inputs": [ { "name": "_approved", "type": "address" }, { "name": "_tokenId", "type": "uint256" } ], "name": "approve", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_zombieId", "type": "uint256" } ], "name": "levelUp", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_zombieId", "type": "uint256" }, { "name": "_kittyId", "type": "uint256" } ], "name": "feedOnKitty", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "zombies", "outputs": [ { "name": "name", "type": "string" }, { "name": "dna", "type": "uint256" }, { "name": "level", "type": "uint32" }, { "name": "readyTime", "type": "uint32" }, { "name": "winCount", "type": "uint16" }, { "name": "lossCount", "type": "uint16" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "withdraw", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "getZombiesByOwner", "outputs": [ { "name": "", "type": "uint256[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "zombieToOwner", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_address", "type": "address" } ], "name": "setKittyContractAddress", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_zombieId", "type": "uint256" }, { "name": "_newDna", "type": "uint256" } ], "name": "changeDna", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_tokenId", "type": "uint256" } ], "name": "ownerOf", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_name", "type": "string" } ], "name": "createRandomZombie", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_from", "type": "address" }, { "name": "_to", "type": "address" }, { "name": "_tokenId", "type": "uint256" } ], "name": "transferFrom", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": true, "inputs": [], "name": "getAllZombies", "outputs": [ { "name": "", "type": "uint256[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, "constant": false, "inputs": [ { "name": "_zombieId", "type": "uint256" }, { "name": "_newName", "type": "string" } ], "name": "changeName", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_fee", "type": "uint256" } ], "name": "setLevelUpFee", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_zombieId", "type": "uint256" }, { "name": "_targetId", "type": "uint256" } ], "name": "attack", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "_from", "type": "address" }, { "indexed": true, "name": "_to", "type": "address" }, { "indexed": false, "name": "_tokenId", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "_owner", "type": "address" }, { "indexed": true, "name": "_approved", "type": "address" }, { "indexed": false, "name": "_tokenId", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "attackResult", "type": "bool" }, { "indexed": false, "name": "winCount", "type": "uint16" }, { "indexed": false, "name": "lossCount", "type": "uint16" } ], "name": "AttackResult", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "zombieId", "type": "uint256" }, { "indexed": false, "name": "name", "type": "string" }, { "indexed": false, "name": "dna", "type": "uint256" } ], "name": "NewZombie", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "previousOwner", "type": "address" }, { "indexed": true, "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" } ]
pragma solidity ^0.4.25; import "./zombieattack.sol"; import "./erc721.sol"; import "./safemath.sol"; contract ZombieOwnership is ZombieAttack, ERC721 { using SafeMath for uint256; mapping (uint => address) zombieApprovals; function balanceOf(address _owner) external view returns (uint256) { return ownerZombieCount[_owner]; } function ownerOf(uint256 _tokenId) external view returns (address) { return zombieToOwner[_tokenId]; } function _transfer(address _from, address _to, uint256 _tokenId) private { ownerZombieCount[_to] = ownerZombieCount[_to].add(1); ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].sub(1); zombieToOwner[_tokenId] = _to; emit Transfer(_from, _to, _tokenId); } function transferFrom(address _from, address _to, uint256 _tokenId) external payable { require (zombieToOwner[_tokenId] == msg.sender || zombieApprovals[_tokenId] == msg.sender); _transfer(_from, _to, _tokenId); } function approve(address _approved, uint256 _tokenId) external payable onlyOwnerOf(_tokenId) { zombieApprovals[_tokenId] = _approved; emit Approval(msg.sender, _approved, _tokenId); } }
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 = randNonce.add(1); return uint(keccak256(abi.encodePacked(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.winCount.add(1); myZombie.level = myZombie.level.add(1); enemyZombie.lossCount = enemyZombie.lossCount.add(1); feedAndMultiply(_zombieId, enemyZombie.dna, "zombie"); } else { myZombie.lossCount = myZombie.lossCount.add(1); enemyZombie.winCount = enemyZombie.winCount.add(1); _triggerCooldown(myZombie); } } }
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 = zombies[_zombieId].level.add(1); } 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.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 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(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"); } }
pragma solidity ^0.4.25; import "./ownable.sol"; import "./safemath.sol"; contract ZombieFactory is Ownable { using SafeMath for uint256; using SafeMath32 for uint32; using SafeMath16 for uint16; 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] = ownerZombieCount[msg.sender].add(1); 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); } }
pragma solidity ^0.4.25; /** * @title Ownable * @dev Il contratto di proprietà ha un indirizzo del proprietario e fornisce funzioni di controllo * delle autorizzazioni di base, ciò semplifica l'implementazione delle "autorizzazioni dell'utente". */ contract Ownable { address private _owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev Il costruttore di proprietà imposta il `proprietario` originale del contratto sull'account del mittente. */ 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; } }
pragma solidity ^0.4.25; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity lo lancia automaticamente quando si divide per 0 uint256 c = a / b; // assert(a == b * c + a % b); // Non esiste un caso in cui ciò non valga return c; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } /** * @title SafeMath32 * @dev SafeMath library implemented for uint32 */ library SafeMath32 { function mul(uint32 a, uint32 b) internal pure returns (uint32) { if (a == 0) { return 0; } uint32 c = a * b; assert(c / a == b); return c; } function div(uint32 a, uint32 b) internal pure returns (uint32) { // assert(b > 0); // Solidity lo lancia automaticamente quando si divide per 0 uint32 c = a / b; // assert(a == b * c + a % b); // Non esiste un caso in cui ciò non valga return c; } function sub(uint32 a, uint32 b) internal pure returns (uint32) { assert(b <= a); return a - b; } function add(uint32 a, uint32 b) internal pure returns (uint32) { uint32 c = a + b; assert(c >= a); return c; } } /** * @title SafeMath16 * @dev SafeMath library implemented for uint16 */ library SafeMath16 { function mul(uint16 a, uint16 b) internal pure returns (uint16) { if (a == 0) { return 0; } uint16 c = a * b; assert(c / a == b); return c; } function div(uint16 a, uint16 b) internal pure returns (uint16) { // assert(b > 0); // Solidity lo lancia automaticamente quando si divide per 0 uint16 c = a / b; // assert(a == b * c + a % b); // Non esiste un caso in cui ciò non valga return c; } function sub(uint16 a, uint16 b) internal pure returns (uint16) { assert(b <= a); return a - b; } function add(uint16 a, uint16 b) internal pure returns (uint16) { uint16 c = a + b; assert(c >= a); return c; } }
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; }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CryptoZombies front-end</title> <script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script language="javascript" type="text/javascript" src="web3.min.js"></script> <script language="javascript" type="text/javascript" src="cryptozombies_abi.js"></script> </head> <body> <script> var cryptoZombies; function startApp() { var cryptoZombiesAddress = "YOUR_CONTRACT_ADDRESS"; cryptoZombies = new web3js.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress); } window.addEventListener('load', function() { // Verifica se Web3 è stato iniettato dal browser (Mist/MetaMask) if (typeof web3 !== 'undefined') { // Usa il provider Mist/MetaMask web3js = new Web3(web3.currentProvider); } else { // Gestire il caso in cui l'utente non ha installato Metamask // Probabilmente mostrare loro un messaggio che richiede di installare Metamask } // Ora puoi avviare la tua app e accedere a web3 liberamente: startApp() }) </script> </body> </html>

Ora che abbiamo inizializzato Web3.js con il provider Web3 di MetaMask, impostiamolo per comunicare con il nostro contratto intelligente.

Web3.js avrà bisogno di 2 cose per parlare del tuo contratto: il suo indirizzo ed il suo ABI.

Indirizzo del Contratto

Dopo aver finito di scrivere il contratto intelligente, lo compilerai e lo distribuirai su Ethereum. Tratteremo la distribuzione (deployment) nella prossima lezione, ma dato che si tratta di un processo abbastanza diverso dalla scrittura del codice, abbiamo deciso di farti scoprire prima Web3.js.

Dopo aver distribuito il contratto, si ottiene un indirizzo fisso su Ethereum dove esso vivrà per sempre. Se ricordi dalla lezione 2, l'indirizzo del contratto CryptoKitties su mainnet Ethereum è 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d.

Dopo la distribuzione dovrai copiare questo indirizzo per poter comunicare con il tuo contratto intelligente.

Contratto ABI

Web3.js parlerà con il tuo contratto tramite la sua ABI.

ABI è l'acronimo di Application Binary Interface. Fondamentalmente è una rappresentazione dei metodi dei tuoi contratti in formato JSON che dice a Web3.js come formattare le chiamate alle funzioni in modo che il tuo contratto lo possa interpretare correttamente.

Quando compili il tuo contratto da distribuire su Ethereum (che tratteremo nella Lezione 7), il compilatore di Solidity ti fornirà l'ABI, dovrai quindi copiarlo e salvarlo in aggiunta all'indirizzo del contratto.

Dato che non abbiamo ancora affrontato la distribuzione, per questa lezione abbiamo compilato l'ABI per te e l'abbiamo inserito in un file chiamato cryptozombies_abi.js, memorizzato in una variabile chiamata cryptoZombiesABI.

Se includiamo cryptozombies_abi.js nel nostro progetto, saremo in grado di accedere all'ABI CryptoZombies usando quella variabile.

Istanza di un contratto Web3.js

Una volta che hai l'indirizzo del tuo contratto e l'ABI, puoi istanziarlo in Web3 come segue:

// Crea un'istanza myContract
var myContract = new web3js.eth.Contract(myABI, myContractAddress);

Facciamo una prova

  1. Nell'<head> del nostro documento, includi un altro tag script per cryptozombies_abi.js in modo da poter importare la definizione ABI nel nostro progetto.

  2. All'inizio del nostro tag <script> in <body>, dichiara un var chiamato cryptoZombies ma non impostarlo uguale a niente. Successivamente utilizzeremo questa variabile per archiviare il nostro contratto istanziato.

  3. Quindi crea una function chiamata startApp(). Compileremo il body nei prossimi 2 passaggi.

  4. La prima cosa che startApp() dovrebbe fare è dichiarare un var chiamato cryptoZombiesAddress ed impostarlo uguale alla stringa "YOUR_CONTRACT_ADDRESS" (questo è l'indirizzo del contratto CryptoZombies sulla mainnet).

  5. Infine istanziamo il nostro contratto. Imposta cryptoZombies uguale ad un nuovo web3js.eth.Contract come abbiamo fatto nel codice di esempio quà sopra. (Usando cryptoZombiesABI, che viene importato con il nostro tag script, e cryptoZombiesAddress da sopra).