Hi everyone, these are my thoughts on the ERC721 assignment:
-
Time complexity for creating a new cat is constant. Doesnât really matter how long the kitties array is since weâre pushing to it, and the new id is derived by kitties.length-1
.
-
getAllCatsFor()
function is linear since it depends on the size of the kitties array due to the for loop.
This is my code to make the getAllCatsFor()
constant.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
contract Kittycontract {
string public constant name = "TestKitties";
string public constant symbol = "TK";
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Birth(
address owner,
uint256 kittenId,
uint256 mumId,
uint256 dadId,
uint256 genes
);
struct Kitty {
uint256 genes;
uint64 birthTime;
uint32 mumId;
uint32 dadId;
uint16 generation;
}
Kitty[] kitties;
mapping (uint256 => address) public kittyIndexToOwner;
mapping (address => uint256) ownershipTokenCount;
mapping (address => uint[]) catsForOwner;
function balanceOf(address owner) external view returns (uint256 balance){
return ownershipTokenCount[owner];
}
function totalSupply() public view returns (uint) {
return kitties.length;
}
function ownerOf(uint256 _tokenId) external view returns (address){
return kittyIndexToOwner[_tokenId];
}
function transfer(address _to,uint256 _tokenId) external{
require(_to != address(0));
require(_to != address(this));
require(_owns(msg.sender, _tokenId));
_transfer(msg.sender, _to, _tokenId);
}
function getAllCatsFor(address _owner) external view returns (uint[] memory cats){
cats = catsForOwner[_owner];
}
function createKittyGen0(uint256 _genes) public returns (uint256) {
return _createKitty(0, 0, 0, _genes, msg.sender);
}
function _createKitty(
uint256 _mumId,
uint256 _dadId,
uint256 _generation,
uint256 _genes,
address _owner
) private returns (uint256) {
Kitty memory _kitty = Kitty({
genes: _genes,
birthTime: uint64(block.timestamp),
mumId: uint32(_mumId),
dadId: uint32(_dadId),
generation: uint16(_generation)
});
kitties.push(_kitty);
uint256 newKittenId = kitties.length - 1;
catsForOwner[_owner].push(newKittenId);
emit Birth(_owner, newKittenId, _mumId, _dadId, _genes);
_transfer(address(0), _owner, newKittenId);
return newKittenId;
}
function _transfer(address _from, address _to, uint256 _tokenId) internal {
ownershipTokenCount[_to]++;
kittyIndexToOwner[_tokenId] = _to;
catsForOwner[_to].push(_tokenId);
if (_from != address(0)) {
ownershipTokenCount[_from]--;
for (uint i=0; i<catsForOwner[_from].length; i++) {
uint idToRemove = _tokenId;
uint lastId = catsForOwner[_from][catsForOwner[_from].length -1];
if (catsForOwner[_from][i] == idToRemove){
catsForOwner[_from][i] = lastId;
catsForOwner[_from].pop();
}
}
}
// Emit the transfer event.
emit Transfer(_from, _to, _tokenId);
}
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return kittyIndexToOwner[_tokenId] == _claimant;
}
}
I made a new mapping to map an address to an array of owned kitties. By doing so, the getAllCatsFor()
function only needs the owner address to retrieve the corresponding array with the kitties.
The _createKitty() function pushes the new Id to the corresponding array catsForOwner[_owner].push(newKittenId)
.
The problem is that now the _transfer() function has to loop through the array of an address in order to find and eliminate the ID of the kitty thatâs been transferred.
Some feedback would be super welcomed since I have no idea if this is a good solution, itâs the first idea I had. Also, Iâm not sure if the _transfer() function has been updated correctly.
Another question I had is on the Kitty object thatâs gets created from the struct in the _createKitty() function. What is the reason behind it, as opposed to just using the Kitty struct?
This is a long answer I guess, so thanks in advance