Index
- Lesson 1: CryptoZombies
- Chapter 2 Contracts
- Chapter 3: State Variables & Integers
- Chapter 4: Math Operations
- Chapter 5: Structs
- Chapter 6: Arrays
- Chapter 7: Function Declarations
- Chapter 8: Working With Structs and Arrays
- Chapter 9: Private / Public Functions
- Chapter 10: More on Functions
- Chapter 11: Keccak256 and Typecasting
- Chapter 12: Putting It Together
- Chapter 13: Events
- Chapter 14: Web3.js
- Lesson 2: Zombies Attack Their Victims
- Chapter 2: Mappings and Addresses
- Chapter 3: Msg.sender
- Chapter 4: Require
- Chapter 5: Inheritance
- Chapter 6: Import
- Chapter 7: Storage vs Memory
- Chapter 8: Zombie DNA
- Chapter 9: More on Function Visibility
- Chapter 10: What Do Zombies Eat?
- Chapter 11: Using an Interface
- Chapter 12: Handling Multiple Return Values
- Chapter 13: Bonus: Kitty Genes
- Chapter 14: Wrapping It Up
- Lesson 3: Advanced Solidity Concepts
- Chapter 2: Ownable Contracts
- Chapter 3: onlyOwner Function Modifier
- Chapter 4: Gas
- Chapter 5: Time Units
- Chapter 6: Zombie Cooldowns
- Chapter 7: Public Functions & Security
- Chapter 8: More on Function Modifiers
- Chapter 9: Zombie Modifiers
- Chapter 10: Saving Gas With 'View' Functions
- Chapter 11: Storage is Expensive
- Chapter 12: For Loops
- Chapter 13: Wrapping It Up
- Lesson 4: Zombie Battle System
- Chapter 1: Payable
- Chapter 2: Withdraws
- Chapter 3: Zombie Battles
- Chapter 4: Random Numbers
- Chapter 5: Zombie Fightin'
- Chapter 6: Refactoring Common Logic
- Chapter 7: More Refactoring
- Chapter 8: Back to Attack!
- Chapter 9: Zombie Wins and Losses
- Chapter 10: Zombie Victory 😄
- Chapter 11: Zombie Loss 😞
- Lesson 5: ERC721 & Crypto-Collectibles
- Chapter 1: Tokens on Ethereum
- Chapter 2: ERC721 Standard, Multiple Inheritance
- Chapter 3: balanceOf & ownerOf
- Chapter 4: Refactoring
- Chapter 5: ERC721: Transfer Logic
- Chapter 6: ERC721: Transfer Cont'd
- Chapter 7: ERC721: Approve
- Chapter 8: ERC721: Approve
- Chapter 9: Preventing Overflows
- Chapter 10: SafeMath Part 2
- Chapter 11: SafeMath Part 3
- Chapter 12: SafeMath Part 4
- Chapter 13: Comments
- Chapter 14: Wrapping It Up
- App Front-ends & Web3.js
- Chapter 1: Intro to Web3.js
- Chapter 2: Web3 Providers
- Chapter 3: Talking to Contracts
- Chapter 4: Calling Contract Functions
- Chapter 5: Metamask & Accounts
- Chapter 6: Displaying our Zombie Army
- Chapter 7: Sending Transactions
- Chapter 8: Calling Payable Functions
- Chapter 9: Subscribing to Events
- Chapter 10: Wrapping It Up
Chapter 4: Calling Contract Functions
Chapter 4: Calling Contract Functions
Our contract is all set up! Now we can use Web3.js to talk to it.
Web3.js has two methods we will use to call functions on our contract:
call
and send
.Call
call
is used for view
and pure
functions. It only runs on the local node, and won't create a transaction on the blockchain.Review:
view
and pure
functions are read-only and don't change state on the blockchain. They also don't cost any gas, and the user won't be prompted to sign a transaction with MetaMask.Using Web3.js, you would
call
a function named myMethod
with the parameter 123
as follows:myContract.methods.myMethod(123).call()
Send
send
will create a transaction and change data on the blockchain. You'll need to use send
for any functions that aren't view
or pure
.Note:
send
ing a transaction will require the user to pay gas, and will pop up their Metamask to prompt them to sign a transaction. When we use Metamask as our web3 provider, this all happens automatically when we call send()
, and we don't need to do anything special in our code. Pretty cool!Using Web3.js, you would
send
a transaction calling a function named myMethod
with the parameter 123
as follows:myContract.methods.myMethod(123).send()
The syntax is almost identical to call()
.Getting Zombie Data
Now let's look at a real example of using
call
to access data on our contract.Recall that we made our array of zombies
public
:Zombie[] public zombies;
In Solidity, when you declare a variable public
, it automatically creates a public "getter" function with the same name. So if you wanted to look up the zombie with id 15
, you would call it as if it were a function: zombies(15)
.Here's how we would write a JavaScript function in our front-end that would take a zombie id, query our contract for that zombie, and return the result:
Note: All the code examples we're using in this lesson are using version 1.0 of Web3.js, which uses promises instead of callbacks. Many other tutorials you'll see online are using an older version of Web3.js. The syntax changed a lot with version 1.0, so if you're copying code from other tutorials, make sure they're using the same version as you!
function getZombieDetails(id) {
return cryptoZombies.methods.zombies(id).call()
}
// Call the function and do something with the result:
getZombieDetails(15)
.then(function(result) {
console.log("Zombie 15: " + JSON.stringify(result));
});
return cryptoZombies.methods.zombies(id).call()
}
// Call the function and do something with the result:
getZombieDetails(15)
.then(function(result) {
console.log("Zombie 15: " + JSON.stringify(result));
});
Let's walk through what's happening here.cryptoZombies.methods.zombies(id).call()
will communicate with the Web3 provider node and tell it to return the zombie with index id
from Zombie[] public zombies
on our contract.Note that this is asynchronous, like an API call to an external server. So Web3 returns a promise here. (If you're not familiar with JavaScript promises... Time to do some additional homework before continuing!)
Once the promise resolves (which means we got an answer back from the web3 provider), our example code continues with the
then
statement, which logs result
to the console.result
will be a javascript object that looks like this:{
"name": "H4XF13LD MORRIS'S COOLER OLDER BROTHER",
"dna": "1337133713371337",
"level": "9999",
"readyTime": "1522498671",
"winCount": "999999999",
"lossCount": "0" // Obviously.
}
"name": "H4XF13LD MORRIS'S COOLER OLDER BROTHER",
"dna": "1337133713371337",
"level": "9999",
"readyTime": "1522498671",
"winCount": "999999999",
"lossCount": "0" // Obviously.
}
We could then have some front-end logic to parse this object and display it in a meaningful way on the front-end.Put it to the Test
We've gone ahead and copied
getZombieDetails
into the code for you.1. Let's create a similar function for
zombieToOwner
. If you recall from ZombieFactory.sol
, we had a mapping that looked like:mapping (uint => address) public zombieToOwner;
Define a JavaScript function called zombieToOwner
. Similar to getZombieDetails
above, it will take an id
as a parameter, and will return a Web3.js call
to zombieToOwner
on our contract.2. Below that, create a third function for
getZombiesByOwner
. If you recall from ZombieHelper.sol
, the function definition looked like this:function getZombiesByOwner(address _owner)
Our function getZombiesByOwner
will take owner
as a parameter, and return a Web3.js call
to getZombiesByOwner
.<!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);
}
function getZombieDetails(id) {
return cryptoZombies.methods.zombies(id).call()
}
// 1. Define `zombieToOwner` here
function zombieToOwner(id) {
return cryptoZombies.methods.zombieToOwner(id).call()
}
// 2. Define `getZombiesByOwner` here
function getZombiesByOwner(owner) {
return cryptoZombies.methods.getZombiesByOwner(owner).call()
}
window.addEventListener('load', function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
} else {
// Handle the case where the user doesn't have Metamask installed
// Probably show them a message prompting them to install Metamask
}
// Now you can start your app & access web3 freely:
startApp()
})
</script>
</body>
</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);
}
function getZombieDetails(id) {
return cryptoZombies.methods.zombies(id).call()
}
// 1. Define `zombieToOwner` here
function zombieToOwner(id) {
return cryptoZombies.methods.zombieToOwner(id).call()
}
// 2. Define `getZombiesByOwner` here
function getZombiesByOwner(owner) {
return cryptoZombies.methods.getZombiesByOwner(owner).call()
}
window.addEventListener('load', function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
} else {
// Handle the case where the user doesn't have Metamask installed
// Probably show them a message prompting them to install Metamask
}
// Now you can start your app & access web3 freely:
startApp()
})
</script>
</body>
</html>