-
What is the time complexity of creating new cats? (constant or linear w nr of cats).
The time complexity of creating new cats is constant. There is no looping involved. -
What is the time complexity of the getAllCatsFor function? (constant or linear w nr of cats).
The time complexity for getAllCatsFor is linear with the number of cats already in existence, because it requires looping through the kitties array every time it is run. The larger the array, the longer the process will take. -
How could the storage design be changed to make the getAllCats function constant? Implement your idea. Then discuss the benefits and drawbacks of your implementation.
I used a mapping from owner addresses to a uint array containing the tokenIds of kittens they own. This change made the getAllCatsFor function time complexity no longer dependent on the total number of cats in existence. However, to implement this solution, I had to make changes in the transfer function which required introducing a loop search through the array of cats owned by the transfer from address. Therefore, the larger the number of cats an individual owner has, the more costly it will be for that owner to transfer a cat to someone else. See code below.
Updated KittyContract1.sol
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 of addresses to arrays containing owned kitties
mapping (address => uint256[]) ownedKitties;
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));
_transferV2(msg.sender, _to, _tokenId);
}
function getAllCatsForV2(address _owner) external view returns (uint[] memory cats){
return ownedKitties[_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);
_transferV2(address(0), _owner, newKittenId);
return newKittenId;
}
function _transferV2(address _from, address _to, uint256 _tokenId) internal {
ownershipTokenCount[_to]++;
kittyIndexToOwner[_tokenId] = _to;
if (_from != address(0)) {
ownershipTokenCount[_from]--;
//Remove _tokenId from ownedKitties[_from] array.
uint lastTokenIdIndex = ownedKitties[_from].length - 1;
uint transTokenIdIndex;
//Find index of token to be removed.
for (uint i = 0; i < ownedKitties[_from].length; i++) {
if (_tokenId == ownedKitties[_from][i]) {
transTokenIdIndex = i;
}
}
//Replace _tokenId to be removed with last tokenId then delete last index
ownedKitties[_from][transTokenIdIndex] = ownedKitties[_from][lastTokenIdIndex];
ownedKitties[_from].pop();
}
//update array of owned kitties in ownedKitties mapping.
ownedKitties[_to].push(_tokenId);
// Emit the transfer event.
emit Transfer(_from, _to, _tokenId);
}
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return kittyIndexToOwner[_tokenId] == _claimant;
}
}