-
constant complexity for creating new cats; it creates a new entry in the array.
-
Linear complexity, complexity increases with the number of kitties, the function getAllCatsFor is looping through each cat defined in the array, which can be overkill when more tokens are available, which affects performance.
-
To solve the issue of performance, I added 2 mappings:
one mapping from Owner Address to an array of token IDs.
the other mapping is from TokenID to its index within the owner array above.
Pros for the solution:
- eliminate the need to loop through the whole kitten struct array to get the list of tokens; instead, it available immediately.
Cons:
- Complexity when performing transfer function to add token IDs to the array and update the index mapping.
- Complexity of handling deletion from token IDs array.
Code below:
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 from owner address to an array containing all tokenIds
mapping (address => uint256[]) ownedKittiesIds;
// mapping from tokenId to the index within owned tokens list (in the ownedKittiesIds).
mapping (uint256=> uint256) ownershipTokenPointer;
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){
//uint[] memory result = new uint[](ownershipTokenCount[_owner]);
//uint counter = 0;
//for (uint i = 0; i < kitties.length; i++) {
// if (kittyIndexToOwner[i] == _owner) {
// result[counter] = i;
// counter++;
//}
//}
//return result;
return ownedKittiesIds[_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;
emit Birth(_owner, newKittenId, _mumId, _dadId, _genes);
_transfer(address(0), _owner, newKittenId);
return newKittenId;
}
function _transfer(address _from, address _to, uint256 _tokenId) internal {
uint256 index;
uint256 lastTokenID;
//increase count of tokens for the to address
ownershipTokenCount[_to]++;
//update ownership address
kittyIndexToOwner[_tokenId] = _to;
if (_from != address(0)) {
//get the index for the deleted token
index=ownershipTokenPointer[_tokenId];
//delete ownedKittiesIds[_from][index];
//get tokenID for the last index
lastTokenID=ownedKittiesIds[_from][ownedKittiesIds[_from].length-1];
//move last index to deleted array position
ownershipTokenPointer[lastTokenID]=index;
ownedKittiesIds[_from][index]=lastTokenID;
ownedKittiesIds[_from].pop();
// add token ID to the ownedKittiesIds array
ownedKittiesIds[_to].push(_tokenId);
index=ownedKittiesIds[_to].length-1;
//store index value for the new token.
ownershipTokenPointer[_tokenId]=index;
ownershipTokenCount[_from]--;
}
// Emit the transfer event.
emit Transfer(_from, _to, _tokenId);
}
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return kittyIndexToOwner[_tokenId] == _claimant;
}
}```