Assignment - Storage Design

hey @cyl2chan its hard to debug from reading your post above. can u paste your code ditectly into a post here and ill take it into remix and have a look for u.

and on your second point. no its completely normal to find it difficult. blockhain dev and specifically solididyt is very challenging and has a steep learning curve in comparison to other areas of software engineering or development. so this is complete;y normal. alls it takes is presistance and eventually you will break though the wall of finding yourself stuck the whole time or reliant on tutorials. everyone goes through this so dont worry

1 Like

one thing i am noticing you will nee to change Entity memory newEntity to Entity memory newEntity = entity

Thanks for the reply! I’ve already change it. It still has some error. May I know what else to do please? Below are my codes:

pragma solidity 0.7.5;
//SPDX-License-Identifier: Unlicense
 
contract mappingContract {
 
    struct Entity{
        uint data;
        address _address;
    }
 
    mapping(address => Entity) public entity;
 
    function addEntity(address _address, uint data) public returns(bool success) {
        Entity memory newEntity = entity;
        newEntity.data = data;
        newEntity._address = msg.sender;
        entity[msg.sender] = newEntity;
        return true;
    }
 
 
    function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
        Entity[msg.sender].data = _data;
        return true;
    }
}
 

In line 23, it said " from solidity:
ETH 201 SC Prog/storageDesign - mapping.sol:23:35: DeclarationError: Undeclared identifier.
Entity[msg.sender].data = _data;
^—^
" as shown in the photo above.

pragma solidity 0.7.5;
//SPDX-License-Identifier: Unlicense
 
contract arrayContract {
 
    struct Entity{
        uint data;
        address _address;
    }
 
    Entity[] public entity;
 
    function addEntity(uint data, address _address) public returns(bool success) {
        Entity memory newEntity = entity;
        newEntity.data = data;
        newEntity._address = msg.sender;
        entity.push(newEntity);
    }
 
    function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
        entity[_index].data = _data;
        return true;
 
}

In line 23, it said "from solidity:
ETH 201 SC Prog/storageDesign - array.sol:25:1: ParserError: Function, variable, struct or modifier declaration expected.

^" as shown in the photo above .

@mcgrane5 @thecil Can someone reply me please? I would appreciate it!

1 Like

Hey,
You should re-read and try to understand what each line of your code are doing. When you do any edit of it, then think and check how this edit will modify the process of your code.

  • Check the use of your last version updateEntity (it only returns true now so that’s not what the function was made for right?)
  • You should always add " _ " at the beginning of input data names, it’s a great habit to take. This makes it easier to read and spot errors due to confusions. Typically here in your struct it would be better to remove " _ " of the address name AND also add it at all your functions input arguments names. You’ll notice that line 15 is confusing without these “_”.
  • The message you received is a warning and not an error.

There are other things that you will have modify so that your code works and provides the result you want to get. Hopefully you’ll find out :wink:

2 Likes

from your screenshot it looks like your missing a closing bracket on your updateEntityFunction. you shuld always be careful to check when you make a function that both brackets are opening. because it can be come a nightmare if you have a missing bracket somehwere especially if you have lodes of nested if and/or for loops

1 Like
  1. When executing the addEntity function, which design consumes the most gas (execution cost)?
    arrays: 88290 gas
    mappings: 66260 gas

  2. Is it a significant difference? Why/why not?
    The mapping saves a significant amount of gas as it’s execution cost is ~25% lower. This is because the mapping requires fewer operations to establish.

  3. Add 5 Entities into storage using the addEntity function and 5 different addresses. Then update the data of the fifth address you used. Do this for both contracts and take note of the gas consumption (execution cost). Which solution consumes more gas and why?
    array: 29348 gas
    mappings: 26933 gas
    The arrays consume more gas as they cycle through the elements of the array until the correct element is accessed.

Mappings

Summary
pragma solidity 0.8.0;

contract StorageMapping {

    struct Entity{
    uint data;
    address _address;
    }

    mapping(address => Entity) entities;

    function addEntity(uint _data) public returns(bool success){
        entities[msg.sender].data = _data;
        entities[msg.sender]._address = msg.sender;
        return true;
    }

    function updateEntity(uint _data) public returns(bool success){
        entities[msg.sender].data = _data;
        return true;
    }

    function viewENtity() public view returns(uint, address){
        return(entities[msg.sender].data, entities[msg.sender]._address);
    }

}

Arrays

Summary
pragma solidity 0.8.0;

contract StorageArray {

    struct Entity{
    uint data;
    address _address;
    }

    Entity[] entities;

     function addEntity(uint _data) public {
        Entity memory anEntity;
        anEntity.data = _data;
        anEntity._address = msg.sender;
        entities.push(anEntity);
    }

    function updateEntity(uint _index, uint _data) public returns(bool success){
        entities[_index].data = _data;
        return true;
    }

    function viewENtity(uint _index) public view returns(Entity memory){
        return (entities[_index]);
    }
}```
2 Likes

When executing the addEntity function, which design consumes the most gas (execution cost)?

Mapping:
execution cost 66054 gas

Array:
execution cost 88190 gas

Is it a significant difference? Why/why not?

Its not a big difference, I dont know why it costs more to push to the array.

Add 5 Entities into storage using the addEntity function and 5 different addresses.
Then update the data of the fifth address you used.
Do this for both contracts and take note of the gas consumption (execution cost).

Mapping:
execution cost 26719 gas

Array:
execution cost 44593 gas

Which solution consumes more gas and why?

The array solution consumes more gas cause you need to loop trough the array before you can update the value.

contract Mapping {

    struct Entity {

        uint data;

        address _address;

    }

    mapping(address => Entity) private entities;

    function addEntity(uint _data) public {

      entities[msg.sender] = Entity(_data, msg.sender);

    }

    function updateEntity(uint _data) public {

        entities[msg.sender].data = _data;

    }

}

contract Array {

    struct Entity {

        uint data;

        address _address;

    }

    Entity[] private entities;

    function addEntity(uint _data) public {

        entities.push(Entity(_data, msg.sender));

    }

    function updateEntity(uint _data) public {

        for(uint i = 0; i < entities.length; i++){

            if(entities[i]._address == msg.sender){

                entities[i].data = _data;

                return;

            }

        }

    }

}
1 Like

This is for Mapping Only. It used 27016 gas.

pragma solidity 0.8.0;

contract onlyMapping{
    struct Entity{
    uint data;
    address _address;
    }

    mapping(address=>Entity) Entities;


    function addEntity(uint _data, address _newAddress) public returns(bool success) {
    Entities[_newAddress].data=_data;
    Entities[_newAddress]._address=_newAddress;
    return true;
    }

    function updateEntity(uint _data, address _addressToUpdate) public returns(bool success){
        Entities[_addressToUpdate].data=_data;
        return true;
    }
}```


This is for Array Only, it used 42201 gas

pragma solidity 0.8.0;

contract onlyArray{

    struct Entity{
    uint data;
    address _address;
    }

    Entity[] entities;

    function addEntity(uint _data, address _newAddress) public returns(bool success){
        entities.push(Entity(_data, _newAddress));
        return true;
    }

    function updateEntity(uint _data, address _addressToUpdate) public returns (bool success){
        for(uint i=0; i<entities.length;i++){
            if(entities[i]._address==_addressToUpdate){
                entities[i].data=_data;
            }
        }
        return true;
    }
}

When updating the 5th transaction; Array version used 56% more Gas than Mapping version.

When adding the 4th transaction for exmple; Array version used 169% more Gas than Mapping version.

1 Like

The array version. Gas to deploy was 287388. Gas to add 5 entities was 429,580. Gas to update was 48,583.

//SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma abicoder v2;

contract StorageSept2022 {
    
    struct EntityStruct{
        uint data;
        address _address;
    }
    
    EntityStruct[] entityArray;
    
    function addEntity(uint _data) public returns(bool success) {
        entityArray.push(EntityStruct(_data, msg.sender));
        return true;
    }

       function updateEntity(uint _data) public {
        for (uint i=0; i<entityArray.length; i++) {
            if (entityArray[i]._address == msg.sender) {
                entityArray[i].data = _data;
                
            }
        }
    }

}
1 Like

Here is the mapping! Gas for deploying: 215,164. Gas for 5 added entities: 380995.
Gas for update: 30,727.
The array was more expensive. I am not sure why, but maybe it is because of the loop and conditional.

//SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma abicoder v2;

contract MappingStorage17September2022 {

    struct Entity {
        uint data;
        address _address;
    }

    mapping(address => Entity) entityMapping;

    function addEntity(uint entityData) public returns (bool) {
        entityMapping[msg.sender]._address = msg.sender;
        entityMapping[msg.sender].data = entityData;
        return true;
    }

   function updateEntity (uint _data) public {
        entityMapping[msg.sender].data = _data;
    }
    
}
1 Like

Mapping Code:

Summary
//SPDX-License-Identifier: UNLICENSED

pragma solidity 0.7.5;

contract Mapping {

    struct Entity{
    uint data;
    address _address;
    }

    mapping(address => Entity) entities;

    function addEntity(uint data) public {
            entities[msg.sender].data = data;
            entities[msg.sender]._address = msg.sender;
        }

    function updateEntity(uint data) public {
            entities[msg.sender].data = data;
        }

    function getEntity() public view returns (uint, address) {
            return (entities[msg.sender].data, entities[msg.sender]._address);
    }

}

Array Code:

Summary
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.5;
pragma abicoder v2;

contract AssignmentArray {

    struct Entity{
    uint data;
    address _address;
    }

    Entity[] entityArray;

    function addEntity(uint data) public {
        Entity memory newEntity;
        newEntity.data = data;
        newEntity._address = msg.sender;
        entityArray.push(newEntity);
    }

    function updateEntity(uint _index, uint data) public {
        require(entityArray[_index]._address == msg.sender);
        entityArray[_index].data = data;
    }

    function getEntity() public view returns(Entity[] memory) {
        return entityArray;
    }

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

}

mapping :
add entry execution cost : 65880
updating entry execution cost : 26553

array :
add entry execution cost : 71190
updating entry execution cost : 31559

Yeah it’s a noticeable difference. The mapping is cheaper.

1 Like

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract GastestMapping {

struct Entity{
address _address;
uint data;

}

mapping(address => Entity) structEntities;

function addEntity(address _address, uint _data) public {
structEntities[_address] = Entity(_address, _data);
}
function updateEntity(address _address, uint _data) public {
structEntities[_address].data = _data;
}
}

pragma solidity ^0.8.4;
contract GastestArray {

struct Entity{
address addresser;
uint data;

}

Entity[] entityList;

function addEntity(address _address, uint _data) public {
entityList.push(Entity(_address, _data));
}
function updateEntity(address _address, uint _data) public {
for(uint i = 0; i < entityList.length; i++)
if (entityList[i].addresser == _address) {
entityList[i].data = _data;
}
}
}

array gas:
gas 46022 gas
|transaction cost 40019 gas
|execution cost 40019 gas

mapping gas:
28162 gas
transaction cost 24488 gas
execution cost 24488 gas

1 Like

You’re not declaring your _index var anywhere?

When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?
an array consumes 22136 or around 1.5 times as more gas then mappings when adding data.
mapping contract consumes - 65876 gas
array contract consumes - 88012 gas

Add 5 Entities into storage using the addEntity function and 5 different addresses. Then update the data of the fifth address you used. Do this for both contracts and take note of the gas consumption (execution cost). Which solution consumes more gas and why?
array consumes more gas then mappings, the reason might be because accessing data from an array requires another piece of data(the index) or to be iterated through the array which would significantly increases the cost for array.
mapping contract consumes - 26541 gas
array contract consumes - 28834 gas

mapping.sol

pragma solidity 0.7.5;

contract Map {

    struct Entity{
    uint data;
    address _address;
    }

    mapping(address => Entity) public entityMap;

    function addEntity(uint _data) public {
        entityMap[msg.sender] = Entity(_data, msg.sender);
    }

    function updateEntity(uint _data) public {
        entityMap[msg.sender].data = _data;
    }
}

array.sol

pragma solidity 0.7.5;

contract List {

    struct Entity{
    uint data;
    address _address;
    }

    Entity[] public entityArray;

    function addEntity(uint _data) public {
        entityArray.push(Entity(_data, msg.sender));
    }

    function updateEntity(uint _index, uint _data) public {
        entityArray[_index].data = _data;
    }
}
1 Like

Here are my results.

First, the code:

// SPDX-License-Identifier: MIT

pragma solidity 0.8.0;

contract simpleMapping {

    struct Entity{
    uint data;
    address _address;
}

  mapping (address => Entity) public entities;

  function addEntity(uint _data) public returns(bool success) {
    Entity memory newEntity;
    newEntity.data = _data;
    newEntity._address = msg.sender;
    entities[msg.sender] = newEntity;
    return true;
  }
  
  function updateEntity(address _updater, uint _data) public returns(bool success) {
    entities[_updater].data = _data;
    return true;
  }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.0;

contract simpleArray {

  struct Entity{
    uint data;
    address _address;
}

  Entity[] public entities;

  function addEntity(uint entityData, address entityAddress) public returns(bool success) {
    Entity memory newEntity;
    newEntity.data = entityData;
    newEntity._address = entityAddress;
    entities.push(newEntity);
    return true;
  }

  function updateEntity(uint _index, uint _data) public returns(bool success) {
    entities[_index].data = _data;
    return true;
  }
}

1/ When executing the addEntity function, which design consumes the most gas (execution cost)?

Simple mapping
gas : 76357
transaction cost : 66397
execution cost : 66397

Simple array
gas : 102493
transaction cost : 89124
execution cost : 89124

Is it a significant difference? 34 % more with array method

Why? With the array, there is an index to set up, not with the mapping ?

2/ Add 5 Entities into storage using the addEntity function and 5 different addresses. Then update the data of the fifth address you used. Do this for both contracts and take note of the gas consumption (execution cost). Which solution consumes more gas and why?

Simple mapping - 2 first transaction
gas : 76357
transaction cost : 66397
execution cost : 66397

Simple mapping
gas : 76371
transaction cost : 66409
execution cost : 66409

Simple array – first transaction
gas : 102493
transaction cost : 89124
execution cost : 89124

Simple array – 2nd transaction
gas : 82828
transaction cost : 72024
execution cost : 72024

Simple array – next ones
gas : 82842
transaction cost : 72036
execution cost : 72036

The array always consumes more gas but the gap is smaller after the first transactions.
I guess this is again due to the indexing of the array.

Array Version:
add: 81769, update: 40234

Map Version
add: 75978, update: 30727

Name: Assessment Array

addEntity()
transaction cost = 89600 gas
execution cost = 68396 gas

updateEntity() for ADDRESS #5 (based on use of loop instead of modifying directly the fifth element)
transaction cost = 42179 gas
execution cost = 20975 gas

pragma solidity 0.8.5;

contract StorageAssignmentArray {

    struct Entity{
        uint data;
        address _address;
    }

    Entity[] public entityArray; //array of structs

    function addEntity(uint entityData) public returns(Entity memory) { //returns the structure
        //we create a new struct and push it to the array
        Entity memory newEntity; //memory is used because we are not storing it in the blockchain
        newEntity._address = msg.sender;
        newEntity.data = entityData;

        //push the structure into the array
        entityArray.push(newEntity); 

        //returns the last structure in the array
        return entityArray[entityArray.length - 1]; 
  }

    function updateEntity(uint newEntityData) public returns (bool success) {
        //we update the data at the row number
        for (uint i = 0; i < entityArray.length; i++) {
            if (entityArray[i]._address == msg.sender) {
                entityArray[i].data = newEntityData;
                return true;
            }
        }
    }
}

Name: Assessment Mapping

addEntity()
transaction cost = 66555 gas
execution cost = 45351 gas

updateEntity() for ADDRESS #5 (no need of loop here)
transaction cost = 26434 gas
execution cost = 5230 gas

pragma solidity 0.8.5;

contract StorageAssignmentMapping {

    struct Entity{
        uint data;
        bool isEntity; //to avoid duplicates
    }

    mapping (address => Entity) public entityMapping; //mapping of address to struct

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

    function addEntity(uint entityData) public returns(bool success) {
        //we require that the address is not already in the mapping
        require(!_isEntity(msg.sender), "Entity already exists"); 

        entityMapping[msg.sender].data = entityData;
        entityMapping[msg.sender].isEntity = true; //set to true to avoid duplicates
    
        return true; //returns true if the entity is added
  }

    function updateEntity(uint newEntityData) public returns (bool success) {

        //we require that the address is already in the mapping
        require(_isEntity(msg.sender), "Entity does not exist");
        //update the data
        entityMapping[msg.sender].data = newEntityData; 

        return true; //returns true if the entity is updated
    }
}
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

contract Mapping {

    mapping(address => uint) public entities;

    function addEntity(uint _data) public {
        entities[msg.sender] = _data;
    }

    function updateEntity(uint _data) public {
        entities[msg.sender] = _data;
    }
}

contract Array {

    struct Entity {
        uint data;
        address _address;
    }

    Entity[] entities;

    function addEntity(uint _data) public {
        entities.push(Entity(_data, msg.sender));
    }

    function updateEntity(uint _data) public {
        for (uint i = 0; i < entities.length; i++) {
            if (entities[i]._address == msg.sender) {
                entities[i].data = _data;
                i = entities.length;
            }
        }
    }
}

Gas costs for addEntity():

address | mapping | array
1st     | 50835   | 101419
2nd     | 50835   | 81754
3rd     | 50835   | 81754
4th     | 50835   | 81754
5th     | 50835   | 81754

The array solution was more than double the gas for the first execution and 61% more expensive than the mapping solution for subsequent executions - this is significant.

Gas costs for updateEntity():
Mapping: 30746
Array: 48717

This operation was 58% more expensive using an array. Mapping is the cheaper solution.

The addEntity() function is more efficient in the mapping version (45173 vs. 49993) although that implementation uses a check function isEntity() to detect doubles . The gas cost is about the same though.
The updateEntity() costs 17925 in the array version and 7856 in the mapping version. The array iteration is of course much more expensive than the mapping lookup. Code:

pragma solidity >=0.8.2 <0.9.0;

/**
 * @title Storage Mapping
 * @dev Store & retrieve value in a variable
 */
contract Storage_mapping {

    struct Entity{
        uint data;
        address _address;
    }

    mapping (address => Entity) public entityStructs;

    /**
     * @dev Checks if the address given is already part of the mapping entityStructs 
     * @param entityAddress address to check
     */
    function isEntity(address entityAddress) public view returns(bool isIndeed) {
        return entityStructs[entityAddress]._address==entityAddress;
    }

    /**
     * @dev Creates a new entity for msg.sender and adds it to the mapping.
     * @param entityData data to store
     */

    function addEntity(uint entityData) public {
       if(isEntity(msg.sender)) revert();
       entityStructs[msg.sender].data=entityData;
       entityStructs[msg.sender]._address=msg.sender;
    }

    /**
     * @dev Updates the data in a saved entity for msg.sender
     * @param entityData data of entity
     */

    function updateEntity (uint entityData) public  {
        if(!isEntity(msg.sender)) revert();
        entityStructs[msg.sender].data=entityData;
    }

}
pragma solidity >=0.8.2 <0.9.0;

/**
 * @title Storage Array
 * @dev Store & retrieve value in a variable
 */
contract Storage_array {

    struct Entity{
        uint data;
        address _address;
    }

     Entity[] public entityStructs;

    /**
     * @dev Creates a new entity for msg.sender and adds it to the array.
     * @param entityData data to store
     */

    function addEntity(uint entityData) public {
       Entity memory _entity;
       _entity.data=entityData;
       _entity._address=msg.sender;
       entityStructs.push (_entity);

    }

    /**
     * @dev Updates the data in a saved entity for msg.sender
     * @param entityData data of entity
     * @return success true if successful, false if not (address not found)
     */

    function updateEntity (uint entityData) public returns (bool success)  {
        for (uint i=0;i<entityStructs.length;i++) {
            if (entityStructs[i]._address==msg.sender) {
                entityStructs[i].data=entityData;
                return true;
            }            
        }
        return false;
    }