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 5: ERC721: Transfer Logic
Chapter 5: ERC721: Transfer Logic
Great, we've fixed the conflict!
Now we're going to continue our ERC721 implementation by looking at transfering ownership from one person to another.
Note that the ERC721 spec has 2 different ways to transfer tokens:
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
and
function approve(address _approved, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
1. The first way is the token's owner calls
transferFrom
with his address
as the _from
parameter, the address
he wants to transfer to as the _to
paramater, and the _tokenId
of the token he wants to transfer.2. The second way is the token's owner first calls
approve
with the address he wants to transfer to, and the _tokenID
. The contract then stores who is approved to take a token, usually in a mapping (uint256 => address)
. Then, when the owner or the approved address calls transferFrom
, the contract checks if that msg.sender
is the owner or is approved by the owner to take the token, and if so it transfers the token to him.If you'll notice, both methods will contain the same transfer logic. In one case the sender of the token calls the function; in the other the owner or the approved receiver of the token calls it.
So it makes sense for us to abstract this logic into its own private function,
_transfer
, which is then called by transferFrom
.Putting it to the Test
Let's define the logic for
_transfer
.1. Define a function named
_transfer
. It will take 3 arguments, address _from
, address _to
, and uint256 _tokenId
. It should be a private
function.2. We have 2 mappings that will change when ownership changes:
ownerZombieCount
(which keeps track of how many zombies an owner has) and zombieToOwner
(which keeps track of who owns what).The first thing our function should do is increment
ownerZombieCount
for the person receiving the zombie (address _to
). Use ++
to increment.3. Next, we'll need to decrease the
ownerZombieCount
for the person sending the zombie (address _from
). Use --
to decrement.4. Lastly, we'll want to change
zombieToOwner
mapping for this _tokenId
so it now points to _to
.5. I lied, that wasn't the last step. There's one more thing we should do.
The ERC721 spec includes a
Transfer
event. The last line of this function should fire Transfer
with the correct information — check erc721.sol
to see what arguments it's expecting to be called with and implement it here.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) {
return ownerZombieCount[_owner];
}
function ownerOf(uint256 _tokenId) external view returns (address) {
return zombieToOwner[_tokenId];
}
// Define _transfer() here
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
}
function approve(address _approved, uint256 _tokenId) external payable {
}
}
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];
}
// Define _transfer() here
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
}
function approve(address _approved, uint256 _tokenId) external payable {
}
}