Storage Design Patterns

One more question here, sorry this lesson’s got me a bit confused. Thi is Filip’s exact code, copied code directly from the github into my Remix.

I am referring to the array_w_unique_ids.sol file. I am having errors compiling it and I believe it is the return entityStructs.push(newEntity) -1; line that is the issue. The error says "operator - not compatible with types tuple() and int _const 1 and it says “different number of arguments in return statement than return declaration” .

Can anyone explain this and the fix for it? I am confused anyway because the header has return (uint rowNumber) and the body we return (newEntity)-1 ?!?! That doesn’t seem consistent. Thanks in advance for the feedback.

pragma solidity 0.8.0;

contract arrayWithUniqueIds {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
  }

  EntityStruct[] public entityStructs;
  mapping(address => bool) knownEntity;

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) revert();
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData = entityData;
    knownEntity[entityAddress] = true;
    return entityStructs.push(newEntity) - 1;
  }

  function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    if(entityStructs[rowNumber].entityAddress != entityAddress) revert();
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }

  function isEntity(address entityAddress) public view returns(bool isIndeed) {
    return knownEntity[entityAddress];
  }

  function getEntityCount() public view returns(uint entityCount) {
    return entityStructs.length;
  }
}
1 Like

Can the Simple_mapping.sol be without the struct EntityStruct?
I also would not need the new entity().

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.0;
contract mappingWithStruct {

  mapping (address => EntityStruct) public entityStructs;
  
  function isEntity(address entityAddress) public view returns(bool isIndeed) {
    return entityStructs[entityAddress].isEntity;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    entityStructs[entityAddress].isEntity = false;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }
}

It would be like above. We would just use the mapping wouldn’t care if it was added or used.
Would it be good? and if not what downsides it would have?

Can anyone be so kind as to explain what the return line in this function is doing? I have gotten it to work but do not understand what it is doing or why it is even necessary?!

function newEntity (address entityAddress, uint entityData, uint entityAge) public returns (EntityStruct memory) {
        EntityStruct memory newEntity;
        newEntity.entityAddress = entityAddress;
        newEntity.entityData= entityData;
        newEntity.entityAge = entityAge;
        entityStructs.push(newEntity);
        return entityStructs[entityStructs.length -1];
    }
1 Like

Hey @Bitborn, hope you are well.

The newEntity function, will add a new position with certain values to the entityStructs array, which should be an array based on the struct.

Now this function ends by returning the position value on the array (entityStructs[entityStructs.length -1];) meaning that after its execution you can verify the array position because it will return the values of it.

But this is just optional if you want to check it, you could just also remove it and have another method to access the array position and check their values. Like:

// will return the values of the array position
function getEntityValues (uint index) public returns (EntityStruct memory) {
        return entityStructs[index];
    }

Hope this helps.

Carlos Z

Thanks for the clarification Carlos. I think I understand but for some reason it’s not returning it’s place in the array. It should be returning this value (index) after I’ve deployed the contract and run the newEntity function right?
I mean if I call the function, enter the correct arguments (address and entityData) it should return me the place in the array (index) after the function has been executed, correct? I should be seeing the return value in remix directly under the newEntity box after i execute it?

Sorry ya’ll one more question here. Can someone break the last line in this function (the combined push and return statement) into 2 separate lines (push on one line and return on the other) so I can see exactly what its doing? The combined statement is throwing an error.

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return entityList.push(entityAddress) - 1;
1 Like

Hi, regarding the UPGRADED Solution lesson, I have some trouble figuring out the code that Filip wrote.
Below is my version of what I understood the storage pattern should do. Could you please help me clarify:

  1. Does the code accomplish what it was supposed to accomplish [is the code usable]?
  2. How can it be improved at this stage, not including the info at the next lesson [if the code is usable]?
	pragma solidity >= 0.7.0 < 0.9.0;
	
	contract StoragePattern {
	    struct Example {
	        uint data;
	        bool isIt;
	    }
	    mapping (address => Example) examples;
        address [] addressesList; // we use an array to save all added addresses [each address has a Struct value]

	    function addStructs(address _addr, uint _data) public returns(uint indexOfStruct) {
	        require(examples[_addr].isIt == false, "This key has already been set!");
	        examples[_addr].data = _data;
	        examples[_addr].isIt = true; // to let us KNOW that we set the values for this struct
			addressesList.push(_addr); // we save the added address to the array
			return addressesList.length - 1; // we return the index of the added address
			// indexOfStruct = addressesList.length - 1;
			// return indexOfStruct;
	    }
		// we can see how many addresses have been set
		function arrayLength() public view returns(uint) {
			return addressesList.length;
		}
	    function isData(address _addr) public view returns(uint, bool isIndeed) {
	        return (examples[_addr].data, examples[_addr].isIt);
	    }
	    function deleteItem(address _addr) public returns(bool success) {
	        require(examples[_addr].isIt == true, "You can only delete already set keys!");
	        delete(examples[_addr].data); // we reset the data to its INITIAL VALUE: 0
	        delete(examples[_addr].isIt); // we reset the bool to its INITIAL VALUE: false
	        //examples[_addr].isIt = false; // to let us KNOW that WE DELETED THIS ITEM no matter its data
	        return true;
	    }
	}

Hi RCV,

The following statement checks if the array is empty (since it this case there length is 0: no values in it), and hence returns false since it means there is no data
if(entityList.length == 0) return false

The next statement sets the pointer to the last value added to the list. “entityList.lenght” gives the number of values in the array (for example we have 4 values), but since arrays are indexed starting by 0 the example of having 4 values doesn’t mean we have index 1 to 4 but rather 0 to 3 (hence the latest value that we set in the pointer is not the length (in our example 4) but rather the index (in the example 3).

entityStructs[entityAddress].listPointer = entityList.length - 1

Hope it is clear :stuck_out_tongue:

1 Like

Hi everyone,

I am having same problem with this return statement throwing an error.
I would really appreciate if someone could clarify what is the following line actually doing and
why am I getting an error:

return entityStructs.push(newEntity) - 1;

Thanks,
Frane

I am getting 2 errors in the Array with Unique IDs contract. Can someone please have a look and suggest the possible reason.

pragma solidity 0.8.0;

contract arrayWithUniqueIds {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
  }

  EntityStruct[] public entityStructs;
  mapping(address => bool) knownEntity;

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) revert();
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData = entityData;
    knownEntity[entityAddress] = true;
    return entityStructs.push(newEntity) - 1;
  }

  function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    if(entityStructs[rowNumber].entityAddress != entityAddress) revert();
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }

  function isEntity(address entityAddress) public view returns(bool isIndeed) {
    return knownEntity[entityAddress];
  }

  function getEntityCount() public view returns(uint entityCount) {
    return entityStructs.length;
  }
}

I am getting errors/warnings in these two lines:

EntityStruct memory newEntity;

return entityStructs.push(newEntity) - 1;

Below are the images with detailed descriptions of each error:

I am confused because my code looks same as Filip’s and I am using same compiler version as he does in his video for Array with Unique IDs

entityStructs[entityAddress].listPointer = entityList.length

So this line is setting the address in the mapping and NOT the listPointer index. Is that right?
If so, what’s the code that sets the listPointer index?

Hello!! I’ve been working on this concept for a different kind of contract (i.e. not using address type as the mapping key). I WAS trying to solve the original error I received with this code, which was the stack overflow (“too many variables”, I thought).

  • I first tried to comment out a number of the variables I was passing into the function from the struct, so that I was only passing 3, and the stack error remained.

  • After researching, I tried to pass the structure into the function, as I read that would only use one slot in the stack, but I then had a lot of other errors.

  • I eliminated most of the errors but ended up with the current error - "TypeError: member “eqSerialNum” not found or not visible after argument-dependent lookup in type (struct CondoService.EquipmentStruct storage pointer) -> pointing to the first instance in the IF statement within the function.

At this point I’m confused as to where to go next.

  1. Is it related to how I’ve passed the struct into the function?
  2. Is it related to not using an address for the mapping key? (I used a uint - Serial Number)

Any assistance would be appreciated!!


//EQUIPMENT object stores all original mechanical, electrical 
//and plumbing EQUIPMENT data
  
  struct EquipmentStruct {
    uint eqIndexNum;
    string eqType;
    uint eqBuilding;
    int eqFloorNum;
    uint eqMechRoomNum;
    string eqManufacturer;
    string eqName;
    uint eqSerialNum;
    string eqColour;
    string eqNFTPhoto;
    uint eqInstallDate;
    uint eqWarrantyDate;
    uint eqWarrantyMonths;
    uint eqEstReplaceDate;
    uint eqEstReplaceCost;
    bool eqOutOfService;
  }

//Equipment mapping and key array (Serial Number)

  mapping(uint => EquipmentStruct) public equipmentMap;
  uint[] public equipmentList;   //creates array of EQUIPMENT by SERIAL NUMBER

//addEQUIPMENT function adds equipment to inventory
  function addEquipment(EquipmentStruct[] memory /*uint _eqIndexNum, string calldata _eqType,
                        uint _eqBuilding, int _eqFloorNum, 
                        uint _eqMechRoomNum, string calldata _eqManufacturer, 
                        string calldata _eqName, uint _eqSerialNum, 
                        string calldata _eqColour, string calldata _eqNFTPhoto, 
                        uint _eqInstallDate, uint _eqWarrantyDate, 
                        uint _eqWarrantyMonths, uint _eqEstReplaceDate,
                        uint _eqEstReplaceCost, bool _eqOutOfService*/) public returns (bool success) {
    
    if(isDuplicateEQ(equipmentMap.eqSerialNum)) revert();
    
    equipmentMap[equipmentMap.eqSerialNum].eqType = equipmentMap.eqType;
    equipmentMap[equipmentMap.eqSerialNum].eqBuilding = equipmentMap.eqBuilding;
    equipmentMap[equipmentMap.eqSerialNum].eqMechRoomNum = equipmentMap.eqMechRoomNum;
    equipmentMap[equipmentMap.eqSerialNum].eqManufacturer = equipmentMap.eqManufacturer;
    equipmentMap[equipmentMap.eqSerialNum].eqName = equipmentMap.eqName;
    equipmentMap[equipmentMap.eqSerialNum].eqSerialNum = equipmentMap.eqSerialNum;
    equipmentMap[equipmentMap.eqSerialNum].eqColour = equipmentMap.eqColour;
    equipmentMap[equipmentMap.eqSerialNum].eqNFTPhoto = equipmentMap.eqNFTPhoto;
    equipmentMap[equipmentMap.eqSerialNum].eqInstallDate = equipmentMap.eqInstallDate;
    equipmentMap[equipmentMap.eqSerialNum].eqWarrantyDate = equipmentMap.eqWarrantyDate;
    equipmentMap[equipmentMap.eqSerialNum].eqWarrantyMonths = equipmentMap.eqWarrantyMonths;
    equipmentMap[equipmentMap.eqSerialNum].eqEstReplaceDate = equipmentMap.eqEstReplaceDate;
    equipmentMap[equipmentMap.eqSerialNum].eqEstReplaceCost = equipmentMap.eqEstReplaceCost;
    equipmentMap[equipmentMap.eqSerialNum].eqOutOfService = equipmentMap.eqOutOfService;

    equipmentList.push(equipmentMap.eqSerialNum);
    equipmentMap[equipmentMap.eqSerialNum].eqIndexNum = equipmentList.length - 1;
    return true;
  }

Your struct contains too many variables, you could remove some and place them into another struct, usually structs does not have more than 7 or 8 variables, to reduce the stack of them.

Carlos Z

1 Like