Assignment - Storage Design

Questions/Answers:

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

Array = 68663 gas (46948 with uint8; 51563 for subsequent additions with uint256) VS Mapping = 23062 gas (23171 with uint8).
Array consumes the most gas, a significant difference; this is because in the EVM, arrays are stored as allocations instead of being stored sequentially in memory (i.e. arrays are ‘less efficient’, in terms of storage and management cost.)

  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?

Array = 10653 gas (8784 with uint8) VS mapping = 5918 gas (6049 with uint8).
Array consumes the most gas; because of the array’s inherent inefficiency compared to the mapping.

Code:

pragma solidity 0.8.0;

contract onlyArrayStorage {

    struct Entity {
        address _address;
        uint data;
    }

    Entity[] public entities;

    function addEntity(address entityAddress, uint entityData) public returns(Entity memory) {
        Entity memory newEntity;
        newEntity._address = entityAddress;
        newEntity.data = entityData;
        entities.push(newEntity);
        return entities[entities.length - 1];
    }

    function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
        entities[rowNumber]._address = entityAddress;
        entities[rowNumber].data = entityData;
        return true;
    }
}

pragma solidity 0.8.0;

contract onlyMappingStorage {

    struct Entity {
        address _address;
        uint data;
    }

    mapping(address => Entity) public entities;

    function addEntity(address entityAddress, uint data) public returns(bool success) {
        entities[entityAddress].data = data;
        return true;
    }

    function updateEntity(address entityAddress, uint data) public returns(bool success) {
        entities[entityAddress].data = data;
        return true;
    }
}
1 Like

my solutions:

for the function with only mapping:

pragma solidity 0.8.0;

contract EntityWithMapping{

    struct Entity{
        uint data;
        address _address;
    }

    mapping (address => Entity) entities;

// when declaring a new variable, solidity will initialize to the default value for datatype
// data = 0
    function addEntity(address newAddress) public{
        require(entities[newAddress].data == 0, "Address already exist!!!");
            entities[newAddress]._address=newAddress;
            entities[newAddress].data +=1;
        
    }
    function updateEntity(address _address, uint newData) public returns(uint) {
        if(entities[_address].data != 0){
            entities[_address].data = newData;
        }
        return newData;
    }

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

for the function with only array:

pragma solidity 0.8.0;

contract EntityWithMapping{

    struct Entity{
        uint data;
        address _address;
    }

    Entity[] public entities;

    function addEntity(address addAddress) public returns (address, uint) {
        bool entityExist = false;
        for(uint i=0;i<entities.length;i++){
            if(entities[i]._address == addAddress ){
                entityExist = true;
                
            }
            require(!entityExist,string(abi.encodePacked(addressToString(addAddress)," already exist!")));
            
        }
        
        if (entityExist!=true){
            Entity memory _entity = Entity({_address: addAddress, data: (entities.length+1)});
            entities.push(_entity);
        }
        return (entities[entities.length-1]._address, entities.length);
    }

    function updateEntity(address addToChange, uint changeData) public{
        bool entityChange = false;
        for (uint i=0;i<entities.length;i++){
            if(entities[i]._address == addToChange){
                entities[i].data = changeData;
                entityChange = true;
            }
        }
        require(entityChange,string(abi.encodePacked(addressToString(addToChange)," not found")));

    }
    // Helper function to convert address to string
    function addressToString(address _addr) internal pure returns (string memory) {
        bytes32 value = bytes32(uint256(uint160(_addr)));
        bytes memory alphabet = "0123456789abcdef";

        bytes memory str = new bytes(42);
        str[0] = '0';
        str[1] = 'x';
        for (uint i = 0; i < 20; i++) {
            str[2 + i * 2] = alphabet[uint(uint8(value[i + 12] >> 4))];
            str[3 + i * 2] = alphabet[uint(uint8(value[i + 12] & 0x0f))];
        }
        return string(str);
    }
}

comparing them, mapping save a lot of gas, less logic and algoritme needed.

1 Like