Assignment - Storage Design

Here are my contracts:

Mapping design:

pragma solidity >=0.7.0 <0.9.0;

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

    mapping(address => Entity) entities;
    
    // Creates a new entity for msg.sender and adds it to the mapping/array.
    function addEntity(uint data) public {
        require(entities[msg.sender]._address == address(0), "Entity already exists");
        
        entities[msg.sender] = Entity(data, msg.sender);
    }
    
    // Updates the data in a saved entity for msg.sender
    function updateEntity(uint data) public {
        require(entities[msg.sender]._address != address(0), "Missing entity");
        entities[msg.sender] = Entity(data, msg.sender);
    }
}

Array design:

pragma solidity >=0.7.0 <0.9.0;

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

    Entity[] entities;
    
    // Creates a new entity for msg.sender and adds it to the array.
    function addEntity(uint data) public {
        (bool found, uint idx) = _findIndex(msg.sender);
        require(!found, "Entity already exists");
        
        entities.push(Entity(data, msg.sender));
    }
    
    // Updates the data in a saved entity for msg.sender
    function updateEntity(uint data) public {
        (bool found, uint idx) = _findIndex(msg.sender);
        require(found, "Missing Entity");
     
        entities[idx] = Entity(data, msg.sender);
    }
    
    function _findIndex(address addr) private returns(bool, uint) {
         for (uint idx=0; idx<entities.length; idx++) {
             if (entities[idx]._address == addr) return (true, idx);
         }
         
         return (false, 0);
    }
}

Results:

  • The Array design consumes more gas because it requires iterating through the array to search for the element to update. Iterating the array is also required for add operation if you want to ensure no duplicate entries in the array exist.
  • Updating the last entry in the array is significantly faster in the mapping design and the difference in gas consumption grows linearly with the size of the array.
    For example: updating the 5th element of the array requires 32% more gas than updating the 1st but updating the 2nd element only requires 8% more gas than the 1st.
1 Like

When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?
In array the addEntity function was: 89488 gas
In mapping the addEntity function was: 43731 gas

Array is significantly more expensive because the function is more complex than the mapping function. Also it uses more storage by making an array from a struct, while mapping accesses the struct directly.

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 uses most gas on the first 3 entries into the array. From the fourth and on the price of Array is slightly lower than Mapping. Please explain me why this is.

Array
updateEntity 1: 29336
updateEntity 2: 29348
updateEntity 3: 29348
updateEntity 4: 26548
updateEntity 5: 26548

Mapping
updateEntity 1: 26587
updateEntity 2: 26587
updateEntity 3: 26587
updateEntity 4: 26587
updateEntity 5: 26587

Array Code

pragma solidity 0.7.5;
pragma abicoder v2;


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


Entity[] public Entities;


function addEntity(uint _data) public returns(Entity memory) {
    Entity memory newEntity;
    newEntity.data    = _data;
    newEntity._address = msg.sender;
   Entities.push(Entity(_data, msg.sender));
    return Entities[Entities.length - 1];
  }


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

    
}

Mapping Code

pragma solidity 0.7.5;



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


mapping (address => Entity) public entities;

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

    
}

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

      
}


    
}

2 Likes

Firstly, here are the 2 code blocks for the individual Storage Structures:

Mapping Storage Design:

pragma solidity 0.8.0;

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

mapping(address => Entity) public mapStorage;

function addEntity(uint _data) public returns (bool added) {
    require (mapStorage[msg.sender].data == 0);
    mapStorage[msg.sender].data = _data;
    return true;
}

function updateEntity(uint _data) public returns (bool updated) {
    require (mapStorage[msg.sender].data != 0);
    mapStorage[msg.sender].data = _data;
    return true;
    }

}

Array Storage Design:

pragma solidity 0.8.0;

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

Entity[] arrayStorage;

function addEntity(uint _data) public returns (bool added){
    Entity memory newStruct;
    newStruct.data = _data;
    newStruct._address = msg.sender;
    arrayStorage.push(newStruct);
    delete newStruct;
    return true;
}

function updateEntity(uint _data) public returns (uint updated) {
    for (uint i = 0; i < arrayStorage.length; i++) {
        if (arrayStorage[i]._address == msg.sender) {
            arrayStorage[i].data = _data;
            return _data;
            }
        }
    }
    
function checkData() public view returns (uint answer) {
    for (uint i = 0; i < arrayStorage.length; i++) {
        if (arrayStorage[i]._address == msg.sender) {
            return arrayStorage[i].data;
            }
        }
    }
    
}

:one: When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?

  • Mapping Storage addEntity gas cost: 44,345 gas
  • Array Storage addEntity gas cost: 88,811 gas

The execution cost for the addEntity function is more than double that of the mapping solution.
This should not come as a surprise, even just by looking at the code.
In the array solution, a temporary struct is created in memory, which is filled with user data and then pushed into the main storage array, and then the memory is cleared by deleting the temporary struct.

In the instance of the mapping however, the entire process is assigning the _data input to the msg.sender address, which can then be discovered by calling the address in the mapping.

:two: 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?

  • Mapping Storage updateEntity gas cost: 27,042 gas
  • Array Storage updateEntity gas cost: 42,195 gas

The Array Storage solution consumes more gas than the mapping solution. This is because the VM needs to loop through the storage array to find the correct struct, and then update the data inside of it.

Alternatively the mapping solution maps directly to the address, and then reassigns new data to the mapped address.

Quite frankly, I expected the difference to be larger than it was and I expected the looping cost in the array would be over 100,000 gas.

1 Like

That’s an interesting observation and I’d be interested in finding out why it’s less gas per execution when you push to an array with a length > 3…

Not to be confused with <3 (or :heart:) :grin:

I would guess it’s something to do with binary memory limitations e.g. 2 bits can hold a maximum number of 3, and then perhaps the compiler can increase to a 3 bit array, which holds a max of 7.

So I would expect that adding the 8th Entity to the array would also see a change in the executing gas cost.

It’s also probably that I’m talking out of my ass but it’s the best explanation I can think of.

If anyone else has an idea, feel free to jump on! :blush:

1 Like

Mapping;

pragma solidity 0.7.5;

contract MappingDesign{
    
    struct Entity{
        uint data;
        address _address;
    }
    
    mapping(address => Entity) public Entities;
    
    //Creates a new entity for msg.sender and adds it to the mapping - 66260 gas
    function addEntity(uint _data) public returns(bool success) {
        require(Entities[msg.sender]._address != msg.sender, "This address already as data");
        Entities[msg.sender]._address = msg.sender;
        Entities[msg.sender].data = _data;
        return true;
    }
    
    //Updates the data in a saved entity for msg.sender
    function updateEntity(uint _data) public returns(bool success){
        Entities[msg.sender].data = _data;
        return true;
    }
}

Array;

pragma solidity 0.7.5;

contract ArrayDesign{
    
        struct Entity{
        uint data;
        address _address;
    }
    
    Entity[] public Entities;
    
    //Creates a new entity for msg.sender and adds it to the array - 88275 gas
    function addEntity(uint _data) public returns(bool success) {
        bool existingEntity = false;
        for(uint i = 0; i < Entities.length; i++){
            if(Entities[i]._address == msg.sender){
                existingEntity = true;
                break;
            }    
        }
        require(existingEntity == false, "This address already has data");
        Entities.push(Entity(_data, msg.sender));
        return true;
    }
    
    //Updates the data in a saved entity for msg.sender
    function updateEntity(uint _data) public returns(bool success){
       for(uint i = 0; i < Entities.length; i++){
            if(Entities[i]._address == msg.sender){
                Entities[i].data = _data;
                break;
            }
        }
        return true;
    }
}
  1. When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?

The addEntity function used with an array uses about 33% more gas than the addEntity function used with mapping. This is probably due to the array needing to be initialized.

  1. 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?

Updating the data for the fifth address consumes 55% more gas with the array design. This is most likely due to the update function having to run through each entry in the array to check for the correct address, while with the mapping design, it only needs to lookup the key.

1 Like

My code for the contract using only a mapping is:

pragma solidity >=0.7.0 <0.9.0;


contract Storage_Design{
    
    struct Entity{
        uint _data;
        address _address;
    }
    
    mapping(address => Entity) entityMapping;
    
    function addEntity(uint input_data) public {
        entityMapping[msg.sender]._address=msg.sender;
        entityMapping[msg.sender]._data=input_data;
    }
    
    
    function updateEntity(uint updated_data) public{
        entityMapping[msg.sender]._data=updated_data;
    }
}

My code for the contract using only an array is

pragma solidity >=0.7.0 <0.9.0;


contract Storage_Design{
    
    struct Entity{
        uint _data;
        address _address;
    }
    
    Entity[] public entityArray;
    
    function addEntity(uint input_data) public {
        Entity memory newEntity;
        newEntity._address=msg.sender;
        newEntity._data=input_data;
        entityArray.push(newEntity);
        
    }
    
    
    function updateEntity(uint updated_data) public{
        for(uint i=0; i<=entityArray.length-1; i++){
        if(entityArray[i]._address==msg.sender){
            entityArray[i]._data=updated_data;
        }    
        }
        
    }
}

Regarding the queitions:

Which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?
Ans: The one using an array consumes more gas at adding an entity (I think this is due to
the “push” command I used). The difference was around 70%, very significant.

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?

Once more the one that consumes more gas is the one with an array and it becuase it uses a for loop to find the address it needs to update.

1 Like

Contract StorageDesign_OnlyMapping

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;

contract StorageDesign_OnlyMapping {

    struct Entity {
        uint data;
        address _address;
    }

    mapping(address => Entity) public entities;

    function addEntity(uint newData, address newAddress) public returns (bool) {
        entities[msg.sender].data = newData;
        entities[msg.sender]._address = newAddress;
        return true;
    }

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

Contract StorageDesign_OnlyArray

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;

contract StorageDesign_OnlyArray {

    struct Entity {
        uint data;
        address _address;
    }

    Entity[] entities;

    function addEntity(uint newData) public returns (uint) {
        for (uint i = 0; i < entities.length; i++) {
            if (entities[i]._address == msg.sender) {
                require(false, "Entity already exists");
            }
        }
        entities.push(Entity(newData, msg.sender));
        return entities.length;
    }

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

StorageDesign_OnlyMapping => 62240 gas
StorageDesign_OnlyArray => 82897 gas
Only mapping least gas due less action , no for loop to check if address already exists.

  1. Same results: mapping cost less gas then array version.
2 Likes

Code using a mapping:

pragma solidity 0.7.5;

contract mappingTest {
    
    struct Entity{
        uint data;
        address _address;
    }
    
    mapping (address => Entity) entityMapping;
    
    function addEntity (uint _data) public {
        entityMapping[msg.sender]._address = msg.sender;
        entityMapping[msg.sender].data = _data;
    }
    
    function updateEntity (uint _newData) public {
        entityMapping[msg.sender].data = _newData;
    }
    
    function getEntityMapping () public view returns (address, uint) {
        return (entityMapping[msg.sender]._address, entityMapping[msg.sender].data);
    }

}

Code using an array:

pragma solidity 0.7.5;
pragma abicoder v2;

contract arrayTest {
    
    struct Entity {
        address _address;
        uint data;
    }
    
    Entity[] entityArray;
    
    function addEntity (uint _data) public {
        entityArray.push(Entity(msg.sender, _data));
    }
    
    
    function updateEntity (uint _index, uint _newData) public {
        require (entityArray[_index]._address == msg.sender, "You can only update your own entries");
        entityArray[_index].data = _newData;
    }
    
    function getEntityArray () public view returns (Entity[] memory) {
        return entityArray;
    }
}

  1. Executing the addEntity function with the mapping cost 65,880 gas. With the array it cost 88,190 gas. Adding an entry to the mapping uses about 30% less gas.

  2. After adding 5 entries to each contract, updating the 5th entry in the mapping cost 26,553 gas. Updating the 5th entry in the array cost 31,513 gas. Updating an entry in the mapping uses around 15% less gas.

Overall, the mapping solution is more efficient with gas because entries can be accessed directly with the key, whereas an array needs to be looped through to find the correct entry to update which requires more computations.

1 Like

Array Contract

pragma solidity 0.7.5;

    contract array {
        
        struct Entity{
        uint data;
        address _address;
}
        Entity [] entities;
    
        function addentity (uint entitydata, address entityaddress) public {
            Entity memory newentity; 
            newentity.data = entitydata;
            newentity._address = entityaddress;
            
            entities.push(newentity);
            
        }
        
        function updatedentity(uint index, uint updateddata) public {
        entities[index].data = updateddata;   
        
            
        }
    }

Mapping Contract


`pragma solidity 0.7.5;`

    contract map {
        
        struct entity{
        uint data;
        address _address;
}
`         mapping(address=>entity)entities;`
         
         function addentity (uint entitydata, address entityaddress) public {
             // Create a new object for the user with the given data
             entities[entityaddress].data= entitydata;
             entities[entityaddress]._address= entityaddress;
         }
         
         function updatedentity (uint entitydata, address entityaddress) public {
             // Assign the new provided data withough overwriting anything that may be added in the future
             entities[entityaddress].data = entitydata;
         }
    }

1) When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?

Yes, when executing the addentity function the array contract consumed more at 71416 execution cost opposed to the mapping contract which had an execution cost of 66286.

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?

Array contract
Updated: 28800
Total execution cost: 357080

Mapping contract
Updated: 26957
Total execution cost: 331430

Overall, the array contract consumes more gas due to the arrays being indexed. This requires more processing to transverse the array at the corresponding index. In contrast, mapping has a key to value pairing making it non indexed. This makes mapping more effective in value retrieval allowing it to be more cost effective.

1 Like

For AddEntity() function,
the gas cost for Mapping was 43942 gas
the gas cost for Array was 68010 gas

The gas cost for Mapping was 35% cheaper for Mapping

After adding 5 entities and updating the last entity value the total gas cost are as follows;
Mapping = 331,881 gas
Array = 331,881 gas
Did both contract use Identical gas fee? This does not seem correct!!!

Below are my codes

pragma solidity 0.8.0;

contract StorageMapping {
    
    struct Entity{
        uint data;
        address _address;
    }
    
    mapping (address => Entity) entityStructs;
    
    function addEntity() public returns (address){
        entityStructs[msg.sender]._address= msg.sender;
        return  entityStructs[msg.sender]._address;
    }
    
    function updateEntity(address _address, uint _data) public returns(uint) {
        entityStructs[_address].data = _data;
        return entityStructs[_address].data;
    }
    
    function getEntityData(address _address) public view returns (uint) {
        return entityStructs[_address].data;
    }
    
}
pragma solidity 0.8.0;

contract StorageArray {
    
    struct Entity {
        uint data;
        address _address;
    }
    
    Entity[] EntityArray;
    
    function addEntity() public {
        Entity memory newEntity;
        newEntity._address = msg.sender;
        newEntity.data = 0;
        EntityArray.push(newEntity);
    }
    
    
    function updateEntity(address _address, uint _data) public  {
        uint counter = 0;
        while (EntityArray.length > counter) {
            if (EntityArray[counter]._address == _address) {
                EntityArray[counter].data = _data;
            }
            
            counter = counter +1;
        }
    }
    
    function getEntityData(address _address) public view returns (uint) {
        uint counter = 0;
        while (EntityArray.length > counter) {
            if (EntityArray[counter]._address == _address) {
                return EntityArray[counter].data;
            }
            counter ++;
        }
        
    }
}
1 Like

When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?

Mapping route is cheaper by about 30k wei. For a one off transaction it would probably be fine but something like this that contextually would be used often its probably best to run a mapping route provided you don’t need the functionality provided by the array solution.

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 solution costs more gas because it needs to consider the data that is in the array when doing its calculations/operations. the bigger the array gets the more complex the operation, the more gas cost.

this was a good exercise to get more familiar with both concepts and practice debugging :smiley:

1 Like

Hello,

Below are my solutions for the assignment:

Mapping Option:

pragma solidity 0.8.0;
contract Assignment_mapping{

struct Entity {
    uint data;
    address _address;
    bool isEntity;
}

mapping (address => Entity) public entities;

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

function updateEntity(uint data) public returns(bool success) {
    if(!entities[msg.sender].isEntity) revert();
    entities[msg.sender].data = data;
    return success;
} 

}

Gas costs for mapping
NewEntity:
Transaction cost 80000000 gas
Execution cost 66995 gas

UpdateEntity:
Transaction cost 80000000 gas
Execution cost 29233 gas

And here is my Array option:

pragma solidity 0.8.0;
pragma abicoder v2;

contract Assignment_array{

struct Entity {
    uint data;
    address _address;
}

Entity[] public entities;

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

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

}

Gas costs for array
NewEntity:
Transaction cost 80000000 gas
Execution cost 88290 gas

UpdateEntity:
Transaction cost 80000000 gas
Execution cost 31363 gas

1 Like

Array Storage Code:

pragma solidity 0.8.0;

contract arrayStorageDesign{
    
    
    struct Entity {
        uint data;
        address _address;
    }
    
    Entity[] public entities;
    
    
    
    function addEntity(uint data) public {
        Entity memory addEntity;
        addEntity.data = data;
        addEntity._address = msg.sender;
        entities.push(addEntity) ;
        
    }
    
    
    
   function updateEntity(uint i, uint data) public {
        
        for (uint i ; i < entities.length; i++) {
            entities[i].data = data;
        }
        
        
    }
}

Mapping Storage Code:

pragma solidity 0.8.0;

contract designMapping{
    
    mapping (address => entity) public entities;
    
    struct entity{
        uint data;
        address _address;
    }
    
    
    function addEntity(uint data) public {
        
        entities[msg.sender] = entity(data, msg.sender);
        
    }
    
    function updateEntity(uint data) public {
        
        entities[msg.sender].data = data;
        
    }
    
    
}

addEntity()

Array:

0 - 88,290 gas
1 - 71,190 gas
2 - 71,190 gas
3 - 71,190 gas
4 - 71,190 gas

Update 5th address - 51,587 gas

Mapping:

1 - 66,076 gas
2 - 66,076 gas
3 - 66,076 gas
4 - 66,076 gas
5 - 66,076 gas

Update 5th address - 26,753 gas

  1. Array Storage design consumes more gas when executing the addEntity function.
    The cost is higher probably because of .push() function which pushes data at the end of the array?

  2. Array Storage consumes more gas when updating 5th address. It is because the contract need to loop through the array to find the 5th address and then it can update its data. The more the array grows the more gas it would consume to find data in the array.

1 Like

The code for storage with mapping:

pragma solidity 0.8.4;

contract StructWtihMapping {
    
    struct Entity{
        uint data;
        address _address;
    }
    
    mapping (address => Entity) entityMapping;
    
    function newEntity(uint _data) public {
        Entity memory newEntity;
        entityMapping[msg.sender].data = _data;
        entityMapping[msg.sender]._address = msg.sender;
    }
    
    function updateEntity(uint _data) public{
        entityMapping[msg.sender].data = _data;
    }
}

The code for storage with array:

pragma solidity 0.8.4;

contract StructWtihMapping {
    
    struct Entity{
        uint data;
        address _address;
    }
    
    Entity [] Entities;
    
    function newEntity(uint _data) public {
        Entity memory newEntity;
        newEntity.data = _data;
        newEntity._address = msg.sender;
        Entities.push(newEntity);
    }
    
    function updateEntity(uint index, uint _data) public{
        Entities[index].data = _data;
        Entities[index]._address = msg.sender;
    }
}

When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?

Execution cost newEntity with mapping:

66152 gas

Execution cost newEntity with array:

88290 gas

=> The execution cost for the array is expensier because it takes more space in the blockchain saving an array then saving a mapping.

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?

Cost of updating the 5th address with mapping:
26731 gas

Cost of updating the 5th address with array:
31631 gas

Updating values with arrays turns out to be more expensive then with mapping. This can happen because it requires more processing power to search the index through the array then with a mapping (where the key instantly gets the value), requiring just the key input. And the process to replace the value is also more complex with arrays then with mappings.

1 Like

Storage Design Assignment Attempt:

pragma solidity 0.8.0;

contract entityArray {
    
    struct Entity {
        uint entityData;
        address entityAddress;
    }
    Entity[] public entityList;
    
    function addEntity(uint entityData) public returns(Entity memory){
        Entity memory newEntity;
        newEntity.entityData = entityData;
        newEntity.entityAddress = msg.sender;
        entityList.push(newEntity);
        return newEntity;
    }
    function updateEntity(uint _index, uint _entityData) public returns(bool updated){
        require(entityList[_index].entityAddress == msg.sender);
        entityList[_index].entityData = _entityData;
        return true;
    }
}

Array storage costs:
• Deployment cost: 334696 gas,
• Adding first address data cost: 88814 gas,
• Adding additional address data cost: 71714 gas,
• Updating entity data cost: 31725 gas,
• Updating the fifth entity data cost: 31725 gas.

pragma solidity 0.8.0;

contract entityMapping {
    
    struct Entity {
        uint entityData;
        address entityAddress;
    }
    mapping(address => Entity) entity;
    
    function addEntity(uint entityData) public returns(Entity memory){
        entity[msg.sender].entityData = entityData;
        return entity[msg.sender];
    }
    function updateEntity(uint _entityData) public returns(bool updated){
        require(entity[msg.sender].entityAddress == msg.sender);
        entity[msg.sender].entityData = _entityData;
        return true;
    }
}

Mapping storage costs:
• Deployment cost: 246428 gas,
• Adding first address data cost: 46712 gas,
• Updating entity data cost: 26921 gas,
• Updating the fifth entity data cost: 26921 gas.

As visible above, the mapping contract’s functions are cheaper to execute. This is because the mapping’s key-to-value data structure is ideal for msg.sender to add themselves as an entity and update their data, rather than adding themselves to an always increasing array.

Obviously there are limitations to what can be done with the information in a mapping because it is a key-to-value data structure, meaning we can’t loop through the mapping to find information, we need to know the inputs to get the outputs.

In certain scenarios, a mapping is ideal- and in others, an array is ideal. It is dependent on what the use case of your contract is and the use case of other contracts that may use yours.

1 Like

Mapping Contract:

pragma solidity 0.8.0;

contract mappingdata {
    
    struct Entity {
        uint data;
        address _address;
    }
    
    mapping (address => Entity) entityMapping;
    
    function addEntity (uint _data) public {
        entityMapping[msg.sender].data = _data;
        entityMapping[msg.sender]._address = msg.sender;
    }
    
    function updateEntity (uint _data) public {
        entityMapping[msg.sender].data = _data;
    }
    
    function getEntity() public returns (uint){
        return entityMapping[msg.sender].data;
    }
}

Execution cost: 66070
Add cost: 66058
Edit cost: 26743

Array Contract

pragma solidity 0.8.0;

contract arraydata{
    
    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{
        entityarray[_index].data = _data;
    }
    
}

Execution cost: 88209
Add Cost: 71202
Edit cost: 29158

The execution cost was around 25% higher for the array which is definitely a significant difference. Not only in execution, but also add and edit costs are higher in the array. I assume this to be because it takes longer to get through an array then to quickly find something through mapping.

1 Like

Hi,
Here is my code:

pragma solidity 0.8.0;
contract MapGas {
    struct Entity{
        uint data;
        address _address;
    }

    mapping (address => Entity) public entityS;
    // addEntity() ;
    // updateEntity() ; 

    function addEntity(address _entityAddress, uint _newData) public returns(bool success) {
        entityS[_entityAddress]._address = msg.sender;
        entityS[_entityAddress].data = _newData; 
        return true;
    }

    function updateEntity(address _entityAddress, uint _newData) public returns(bool success) { 
        entityS[_entityAddress]._address = msg.sender;
        entityS[_entityAddress].data = _newData; 
        return true;
    }
} 
pragma solidity 0.8.0;
contract ArrGas {
    struct Entity{
        uint data;
        address _address;
    }

    //mapping (address => Entity) public entityS;
    Entity[] public entityS;
    // addEntity() ;
    // updateEntity() ; 

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

    function updateEntity(uint _idx, uint _newData) public returns(bool success) {  
        entityS[_idx].data = _newData; 
        return true;
    } 
}

Execution cost for mapping:
Insert operation is 66895
Update operation 29852

Execution cost for array:
Insert operation is 72024
Update operation 29336

As far as i understand, the difference between array and mapping is that array has length calculation inside.
When we update there is no operation with legth parameter, so that’s wjy cost is similar.
For insert operation cost for mapping is a bit small cause there is no length calculation.

Hey team,

I have written the contracts for both the mapping and array storage below, but I am having trouble answering the questions because I cannot run and deploy the functions on Remix.

arrayStorage
pragma solidity 0.7.5;

contract arrayStorage {
    
    struct Entity {
        uint data;
        address _address;
    }
    
    Entity[] public EntityList;
    
    function addEntity(uint _data) public returns(bool success) {
        Entity memory newEntity; //Declare new array 
        newEntity.data = _data; // Add data value
        newEntity._address = msg.sender; // Add owner address
        EntityList.push(newEntity); //new entity added/pushed to the array
        return true; //successful?
    }
    
    function updateEntity(uint idx, uint _data) public {
        require(EntityList[idx]._address == msg.sender, 'invalid user');
        EntityList[idx].data = _data;
    }
}
mapStorage
pragma solidity 0.7.5;

contract mappingStorage {
    
    struct Entity {
        uint data;
        address _address;
    }
    
    mapping(address => Entity) public entityStructs;
    
    function addEntity(uint _data) public returns(bool success) {
        Entity memory newEntity; // Declare the storage list
        newEntity.data = _data;
        newEntity._address = msg.sender; // initialize address
        entityStructs[msg.sender] = newEntity; // add new entity to the list
        return true;
    }
    
    function updateEntity(uint _data) public returns(bool success) {
        entityStructs[msg.sender].data = _data;
        return true;
    }
}

When I go to the run and deploy page, you’ll see from the picture below that I do not have the full functionality available to deploy the contracts. I can’t access the parameters I require for the functions in order to find out what the cost of gas is.

Am I doing something wrong, or can someone explain to me how to run the contracts please?

DeployandRun - Remix

  1. addEntity function execution costs
    1.1 Mapping -> 45756 gas
    1.2 Array -> 67888 gas
    Array is ~30% more expensive because it needs to instanciate in storage an heavier object
  2. Add 5 Entities + Update fifth Entity
    2.1 Mapping -> (45756 x 5) + 43641 = 272421 gas
    2.2 Array -> 67888 + (50788 x 4) + 58553 = 329573 gas
    Array is ~17% more expensive because the update function has to loop the all array to find the position of the entity to be updated
    NOTE: also the cost of deplymnet is higher for the array (166357 vs 148951)

Here below the code of my solution:
Mapping:

pragma solidity 0.7.5;

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

mapping (address => Entity) EntityMapping;
    
function addEntity() public {
    EntityMapping[msg.sender].data = 0;
    EntityMapping[msg.sender]._address = msg.sender;
}

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

}

Array:

pragma solidity 0.7.5;

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

Entity[] EntityArray;
    
function addEntity() public {
    EntityArray.push(Entity(0, msg.sender));
}

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

}
1 Like

1. Question:

  • When executing addEntity(), only mapping storage design pattern uses 66058 gas, which is less than design pattern with only array (88920 gas).
  • Difference is 30%, which is quite a lot.

2. Question:

  • Execution cost when updating 5th entity in array with only array storage costs 42246 gas.
  • Execution cost when updating 5th entity with only array storage costs 26741gas.
  • Array solution uses 60% more gas than mapping solution, because it has to loop through array

3. Code:

  • With mapping:
pragma solidity 0.8.0;

contract OnlyMapping {
    
    struct Entity{
        uint data;
        address _address;
    }
    
    mapping(address => Entity) public entityMapping;
    
    function createEntity(uint _data) public {
        entityMapping[msg.sender].data = _data;
        entityMapping[msg.sender]._address = msg.sender;
    }
    
    function updateEntity(uint _newData) public{
        entityMapping[msg.sender].data = _newData;
    }
    
}
  • With array:
pragma solidity 0.8.0;

contract OnlyArray {
    
    struct Entity{
        uint data;
        address _address;
    }
    
    Entity[] public entityArray;
    
    function createEntity(uint _data) public {
        Entity memory newEntity;
        newEntity.data = _data;
        newEntity._address = msg.sender;
        entityArray.push(newEntity);
    }
    
    function updateEntity(uint _newData) public{
        for(uint i = 0; i < entityArray.length; i++){
            if(entityArray[i]._address == msg.sender){
                entityArray[i].data = _newData;
            }
        }
    }
    
}

1 Like