Can someone here help me understand a couple core concepts?
-
I followed Filip’s method of using an external function for checks, then having that external function call an internal “underscore” _function for the actual execution. I noticed that Filip places the emit keyword in the first (external) function. I placed mine in the second (internal) function. Is there anything wrong with doing it that way? What is the best practice and why?
-
My compiler is suggesting that I change the name() and symbol() view functions to pure. Is there any reason to do that?
-
In the case of this project, is there any reason to implement the functions in the IERC721 Interface rather than simply declaring them all in the base contract?
pragma solidity 0.8.0;
// import "./IERC721.sol";
import "./Ownable.sol";
contract Kittycontract is Ownable {
string public constant tickerName = "ThePowerOfMeow";
string public constant tickerSymbol = "MEOW";
uint256 totalTokenCount;
uint256 public constant CREATION_LIMIT_GEN0 = 10;
uint256 public gen0Counter = 0;
event Birth(
address owner,
uint256 kittenId,
uint256 matronId,
uint256 sireId,
uint256 genes
);
struct Kitty {
uint256 genes;
uint64 birthTime;
uint32 matronId;
uint32 sireId;
uint16 generation;
}
Kitty[] kitties;
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
mapping(address => uint256) private ownershipTokenCount;
mapping(uint256 => address) public ownedBy;
mapping(uint256 => address) public kittyIndexToApproved; //address can transfer token on owner's behalf
mapping(address => mapping(address => bool)) private _operatorApprovals; //address can transfer ALL tokens on owner's behalf
function _createKitty(
uint256 _matronId,
uint256 _sireId,
uint256 _generation,
uint256 _genes,
address _owner
) private returns(uint256) {
Kitty memory _kitty = Kitty({
genes: _genes,
birthTime: uint64(block.timestamp),
matronId:uint32(_matronId),
sireId: uint32(_sireId),
generation: uint16(_generation)
});
kitties.push(_kitty);
uint256 newKittenId = kitties.length - 1;
emit Birth(_owner, newKittenId, _matronId, _sireId, _genes);
_transfer(address(0), _owner, newKittenId);
return newKittenId;
}
function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256 id){
require(gen0Counter < CREATION_LIMIT_GEN0, "Maximum Gen0 limit reached");
gen0Counter++;
return _createKitty(0, 0, 0, _genes, msg.sender);
}
function getKitty(uint kittenId) public view returns(Kitty memory) {
return kitties[kittenId];
}
function balanceOf(address owner) external view returns(uint256 balance) {
return ownershipTokenCount[owner];
}
function totalSupply() external view returns (uint256 total) {
return kitties.length;
}
function name() external view returns (string memory tokenName) {
return tickerName;
}
function symbol() external view returns (string memory tokenSymbol) {
return tickerSymbol;
}
function ownerOf(uint256 tokenId) external view returns (address owner) {
address tokenOwner = ownedBy[tokenId];
require(tokenId < kitties.length, "Token does not exist");
return tokenOwner;
}
function transfer(address to, uint256 tokenId) external {
require(to != address(0), "Invalid recipient, cannot transfer to zero addres");
require(to != address(this), "Invalid recipient, cannot transfer to this contract");
require(to != msg.sender, "Invalid recipient, you cannot transfer to yourself");
require(ownedBy[tokenId] == msg.sender, "Transfer function is only for token owner, operator should use transferFrom");
_transfer(msg.sender, to, tokenId);
}
function _transfer(address _from, address _to, uint256 _tokenId) internal {
ownershipTokenCount[_to]++;
if(_from != address(0)){
ownershipTokenCount[_from]--;
delete kittyIndexToApproved[_tokenId];
}
ownedBy[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
function approve(address approved, uint256 tokenId) external {
require(ownedBy[tokenId] != address(0));
require(validateOperator(msg.sender, tokenId), "Only token owner or authorized operator can approve");
_approve(ownedBy[tokenId], approved, tokenId);
}
function _approve(address _owner, address _approved, uint256 _tokenId) internal {
kittyIndexToApproved[_tokenId] = _approved;
emit Approval(_owner, _approved, _tokenId);
}
function setApprovalForAll(address operator, bool approved) external {
require(operator != msg.sender, "Operator must be a third party");
_setApprovalForAll(operator, approved);
}
function _setApprovalForAll(address _operator, bool _approved) internal {
_operatorApprovals[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function getApproved(uint256 tokenId) public view returns (address) {
require(tokenId < kitties.length, "Token does not exist");
return kittyIndexToApproved[tokenId];
}
function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
return _operatorApprovals[_owner][_operator];
}
function transferFrom(address from, address to, uint256 tokenId) external {
require(validateOperator(msg.sender, tokenId) || kittyIndexToApproved[tokenId] == msg.sender, "Unauthorized operator");
require(from == ownedBy[tokenId], "Can only transfer from token owner");
require(to != address(0), "Token has no owner");
require(tokenId < kitties.length, "Token does not exist");
_transfer(from, to, tokenId);
}
function validateOperator(address _operator, uint _tokenId) internal view returns(bool) {
bool validOperator = false;
address tokenOwner = ownedBy[_tokenId];
if(tokenOwner == _operator){
validOperator = true;
}
else if(_operatorApprovals[tokenOwner][_operator] == true){
validOperator = true;
}
return validOperator;
}
}