Hey @Hudson, I fixed my issues with using remix, I backed up all my files as you recommended.
Turns out, my problem was using an ‘https’ connection when I should’ve ‘http’ on my browser instead.
Hey @Hudson, I fixed my issues with using remix, I backed up all my files as you recommended.
Turns out, my problem was using an ‘https’ connection when I should’ve ‘http’ on my browser instead.
When executing the addEntity function, which design consumes the most gas (execution cost)?
-The array design consumes the most gas in execution cost.
Is it a significant difference?
-It’s about a 50% increase in gas consumption to use an array over a mapping, so I’d say it’s pretty significant.
Why/why not?
-Arrays are larger data structures than mappings, even if they contain the same information arrays still have more meta-data that provides their special functionality. Mappings are very stripped down by comparison.
Add 5 Entities into storage using addEntity function and 5 different addresses, then update 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. Again, arrays are more complex structures than mappings and have more functionality that needs to be processed. Also, depending on whether you want to include an index number as an input or not, the gas fee will either be the same for each entry in the array if we use an index input or it will get bigger with each entry if we have to use a loop to find the corresponding entry for msg.sender.
Here’s my code:
pragma solidity 0.7.5;
pragma abicoder v2;
contract StorageDesignMapping {
struct Entity {
uint data;
address _address;
}
mapping (address => Entity) public entityMapping;
function addEntity(uint _data) public {
Entity memory newEntity = Entity(_data, msg.sender);
entityMapping[msg.sender] = newEntity;
}
//execution cost = 41549
//transaction cost = 63013
function updateEntity(uint _data) public {
entityMapping[msg.sender] = Entity(_data, msg.sender);
}
//execution cost = 7294
//transaction cost = 28758
}
contract StorageDesignArray {
struct Entity {
uint data;
address _address;
}
Entity[] public entities;
function addEntity(uint _data) public {
Entity memory newEntity = Entity(_data, msg.sender);
entities.push(newEntity);
}
//execution cost = 62385
//transaction cost = 83849
function updateEntity(uint _index, uint _data) public {
entities[_index] = Entity(_data, msg.sender);
}
//execution cost = 8269
//transaction cost = 29861
}
Dude, brilliant deduction, well done.
This seems to be one of the ghosts that haunts Remix IDE. I was hoping that the Truffle setup videos would be earlier on in this 201 course, literally just to get away from editing in a browser. (Almost there now!)
Cheers friend
I finally fixed my issue with deploying contracts in remix, and here’s my answer to the questions for this assignment:
When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?
From these results, using an Only array solution would cost 37% more gas (execution cost) than using an only mapping solution. 37% is very significant especially when handling larger amounts of data.
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?
pragma solidity 0.8.1;
//create a data storage contract using mapping. Will be looking at
//gas consumption using this method.
contract DataStorageUsingMapping
{
struct Entity{
uint data;
address _address;
}
mapping(address => Entity) public entity;
function addEntity(uint _data) public {
//Entity memory temp = Entity(_data, msg.sender);
//entity[msg.sender] = temp;
entity[msg.sender].data = _data;
entity[msg.sender]._address = msg.sender;
}
function updateEntity(uint _data) public {
entity[msg.sender].data = _data;
}
}
pragma solidity 0.8.1;
//create a data storage contract using an array. Will be looking at
//gas consumption using this method.
contract DataStorageUsingArray
{
struct Entity{
uint data;
address _address;
}
Entity[] public entity;
function addEntity(uint _data) public {
Entity memory temp = Entity(_data, msg.sender);
entity.push(temp);
}
function updateEntity(uint _data) public {
uint index;
//iterate through the array to find the index of the array to update
for(uint i = 0; i < entity.length; i++)
{
if(msg.sender == entity[i]._address)
index = i;
}
entity[index].data = _data;
}
}
Hello everyone,
The code for both contracts is at the bottom of this post.
Execution cost measurements:
Contract | Function | Execution cost (gas) |
---|---|---|
Array | addEntity | Average ~57661 (64157, 51909, 54661, 57413, 60165) |
Array | updateEntity (5th entry) | 20828 |
Mapping | addEntity | 42443 |
Mapping | updateEntity (5th entry) | 6503 |
In the case of both Array and Mapping, addEntity is expensive compared to updateEntity because it involves creating a new element in storage, whilst updateEntity only modifies existing data in storage.
updateEntity is 3.2 times cheaper with the mapping than with the array because the mapping optimises random access. In the case of the array it is necessary to iterate over each element to find the desired entry.
Matt
pragma solidity 0.8.0;
contract MagicArray{
struct Entity{
uint data;
address _address;
}
Entity[] entities;
function addEntity(uint data) public returns (uint length){
for(uint i = 0; i < entities.length; ++i)
{
require(entities[i]._address != msg.sender, "entity already exists");
}
entities.push(Entity(data, msg.sender));
return entities.length;
}
function updateEntity(uint data) public {
uint i;
for(i = 0; i < entities.length && entities[i]._address != msg.sender ; ++i)
{
}
require(i < entities.length, "Entity does not exist");
entities[i].data = data;
}
function getData() public view returns (uint data, uint index){
uint i;
for(i = 0; i < entities.length && entities[i]._address != msg.sender ; ++i)
{
}
require(i < entities.length, "Entity does not exist");
return (entities[i].data, i);
}
}
pragma solidity 0.8.0;
contract MagicMapping{
struct Entity{
uint data;
bool exists;
}
mapping( address => Entity) entities;
function addEntity(uint data) public {
require(entities[msg.sender].exists == false, "This entity already exists");
entities[msg.sender].data = data;
entities[msg.sender].exists = true;
}
function updateEntity(uint data) public {
require(entities[msg.sender].exists == true, "This entity does not exist");
entities[msg.sender].data = data;
}
function getData() public view returns (uint data){
require(entities[msg.sender].exists == true, "This entity does not exist");
return entities[msg.sender].data;
}
}
StorageMapping.sol
pragma solidity 0.7.5;
contract Storage1 {
struct Entity{
uint data;
address _address;
}
mapping (address => Entity) entities;
function addEntity( uint entityData) public returns (bool success) {
entities[msg.sender].data = entityData;
entities[msg.sender]._address = msg.sender;
return true;
}
function updateEntity (uint entityData) public returns (bool success) {
entities[msg.sender].data = entityData;
return true;
}
}
StorageArray.sol
pragma solidity 0.7.5;
contract Storage2 {
struct Entity{
uint data;
address _address;
}
Entity[] public entities;
function addEntity(uint entityData) public returns (bool success){
Entity memory newEntity;
newEntity.data = entityData;
newEntity._address = msg.sender;
entities.push(newEntity);
return true;
}
function updateEntity (uint entityData) public returns (bool success){
for (uint i=0; i<entities.length; i++) {
if(entities[i]._address == msg.sender) {
entities[i].data = entityData;
return true;
}
}
return false;
}
}
Obviously the array solution consumes more gas. The difference is even significant on the update operation, as on the array we need to iterate the whole array until we find the entry we want to update. On contrast, on the mapping solution, we can use the address as a key to find the entry we want, so we do need to iterate through it.
Here’s the code (results below the code):
pragma solidity 0.7.5;
pragma abicoder v2;
contract mappingStorageDesign {
struct Entity {
uint _data;
address _address;
}
mapping(address => Entity) entities;
function addEntity (uint _data) public {
Entity memory newEntity;
newEntity._data = _data;
newEntity._address = msg.sender;
entities[msg.sender] = newEntity;
}
function updateEntity (uint _data) public {
entities[msg.sender]._data = _data;
}
//Simple getter to check if the add/update function works
function getEntity () public view returns (Entity memory){
return entities[msg.sender];
}
}
contract arrayStorageDesign {
struct Entity {
uint _data;
address _address;
}
Entity[] entities;
function addEntity(uint _data) public {
Entity memory newEntity;
newEntity._data = _data;
newEntity._address = msg.sender;
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;
}
}
}
//Simple getter to check if the add/update function works
function getEntity (uint _index) public view returns (Entity memory) {
return entities[_index];
}
}
Results:
addEntity():
Mapping:
transaction cost 63014 gas
execution cost 41550 gas
Array:
transaction cost 68850 gas
execution cost 47386 gas
No significant difference. It costs about the same to add data to a mapping as to an array. Same magnitude of complexity.
updateEntity():
Mapping:
transaction cost 26979 gas
execution cost 5515 gas
Array:
transaction cost 41791 gas
execution cost 20327 gas
The array solution consumes more gas since we must iterate the array to update the entity (linear complexity), while with the mapping we have direct access to data (constant complexity).
pragma solidity 0.8.3;
contract StorageAssignmentMapping {
struct Entity{
uint data;
address _address;
}
mapping (address => Entity) dataStorage;
function addEntity(uint _data) public {
dataStorage[msg.sender].data = _data;
dataStorage[msg.sender]._address = msg.sender;
}
function updateEntity(uint _data) public {
dataStorage[msg.sender].data = _data;
}
function readEntity() public view returns (uint, address) {
return (dataStorage[msg.sender].data, dataStorage[msg.sender]._address);
}
}
contract StorageAssignmentArray {
struct Entity{
uint data;
address _address;
}
Entity[] dataStorage;
function addEntity(uint _data) public {
dataStorage.push(Entity(_data, msg.sender));
}
function updateEntity(uint _id, uint _data) public {
dataStorage[_id].data = _data;
}
function readEntity() public view returns (Entity[] memory) {
return (dataStorage);
}
}
Gas Costs | mapping | array | % more expensive |
---|---|---|---|
Add | 41476 | 62308 | 50.23% |
1 additional | 41476 | 47308 | 14.06% |
1 additional | 41476 | 47308 | 14.06% |
1 additional | 41476 | 47308 | 14.06% |
1 additional | 41476 | 47308 | 14.06% |
1 additional | 41476 | 47308 | 14.06% |
modify 5th | 5537 | 6512 | 17.61% |
Looks like Array is all round more expensive than Mapping for this use case as items need to be stored to ‘storage’ in order to be recalled, where as mapping storage requirements are not as costly.
Initial initializing of the array or mapping are more expensive operations than subsequent modifications to the same mapping or array index.
pragma solidity 0.8.0;
contract StorageDeisgn1{
struct Entity{
uint data;
address _address;
}
mapping (address => Entity) entityList;
function addEntity(uint _data) public {
entityList[msg.sender] = Entity(_data, msg.sender);
}
function updateEntity(uint _data) public {
entityList[msg.sender].data = _data;
}
}
pragma solidity 0.8.0;
contract StorageDeisgn2{
struct Entity{
uint data;
address _address;
}
Entity[] entityList;
function addEntity(uint _data) public{
entityList.push(Entity(_data, msg.sender));
}
function updateEntity(uint index, uint _data) public{
entityList[index].data = _data;
}
}
2.The solution with the array cost more gas because of the needed variable of index to change its data in the struct this adds more memory space to be used which results in more gas consumption
Hello!
Here are my contracts for this assignment:
This is for the mapping:
pragma solidity 0.8.0;
contract EntitiyMapping {
struct Entity {
uint data;
address _address;
}
mapping(uint => Entity) public entityStructs;
function addEntity(uint addressId, address newAddress, uint data) public {
entityStructs[addressId]._address = newAddress;
entityStructs[addressId].data = data;
}
function updateEntity(uint addressId, address newAddress, uint data) public returns (address, uint) {
entityStructs[addressId]._address = newAddress;
entityStructs[addressId].data = data;
return (newAddress, data);
}
}
And this is for the Array one:
pragma solidity 0.8.0;
pragma abicoder v2;
contract EntityArray {
struct Entity {
uint data;
address _address;
}
Entity[] public entityStructs;
function addEntity(uint data, address _address) public returns (Entity[] memory){
Entity memory newEntity;
newEntity._address = _address;
newEntity.data = data;
entityStructs.push(newEntity);
return entityStructs;
}
function updateEntity(address _address) public returns (Entity[] memory){
Entity storage updateEntity = entityStructs[entityStructs.length -1];
updateEntity._address = _address;
return entityStructs;
}
}
And here is the list of execution costs for both contracts:
For Mapping:
Add Entity 1: 41869
Add Entity 2: 41869
Add Entity 3: 41869
Add Entity 4: 41869
Add Entity 5: 41869
Update cost: 8006
And for the Array:
Add Entity 1: 66313
Add Entity 2: 53659
Add Entity 3: 56006
Add Entity 4: 58352
Add Entity 5: 60699
Update Entity cost: 21277
This means that the array createEntity cost is averagely 1.5x more than the mapping one. I am not sure about the why, but is it because that array elements are stored in storage while mapping is not?
Execution cost is also higher for the array one for me, which is around 2.65x more. I only have the idea of explaining this on the same matter as arrays are stored in storage while mappings are not but if there is better explanation, please reply and I will learn it!
pragma solidity 0.8.3;
contract Contract{
struct Entity{
uint data;
address adr;
}
mapping(address => Entity) entities;
function addEntity(uint _data) public {
if(entities[msg.sender].adr == msg.sender) revert();
entities[msg.sender].data = _data;
entities[msg.sender].adr = msg.sender;
}
function updateEntity(uint _data) public {
if(entities[msg.sender].adr != msg.sender) revert();
entities[msg.sender].data = _data;
}
function getEntityData() public view returns(uint) {
if(entities[msg.sender].adr != msg.sender) revert();
return entities[msg.sender].data;
}
}
addEntity: 42422 gas
updateEntity: 6502 gas
pragma solidity 0.8.3;
contract Contract{
struct Entity{
uint data;
address adr;
}
Entity[] entities;
function addEntity(uint _data) public {
uint i=0;
for(;i<entities.length;i++){
if(entities[i].adr == msg.sender) break;
}
require(i==entities.length);
Entity memory newEntity;
newEntity.data = _data;
newEntity.adr = msg.sender;
entities.push(newEntity);
}
function updateEntity(uint _data) public {
uint i=0;
for(;i<entities.length;i++){
if(entities[i].adr == msg.sender) break;
}
require(i<entities.length);
entities[i].data = _data;
}
function getEntityData() public view returns(uint) {
uint i=0;
for(;i<entities.length;i++){
if(entities[i].adr == msg.sender) break;
}
require(i<entities.length);
return entities[i].data;
}
}
addEntity: 64057 gas --> 51814 gas --> 54571gas --> 57328 gas --> 60085 gas
updateEntity: 20818 gas
Array contract:
pragma solidity 0.8.0;
contract StorageDesignArray {
struct Entity{
uint data;
address _address;
}
Entity[] public entityArray;
function addEntity(uint _data) public returns(bool done){
Entity memory newEntity;
newEntity._address = msg.sender;
newEntity.data = _data;
entityArray.push(newEntity);
return true;
}
function updateEntity(uint _id, uint _data) public returns(bool done){
entityArray[_id].data = _data;
return true;
}
}
Mapping contract:
pragma solidity 0.8.0;
contract StorageDesignMapping {
struct Entity{
uint data;
address _address;
}
mapping(address => Entity) public entityMapping;
function addEntity(uint _data) public returns(bool done){
Entity memory newEntity;
newEntity._address = msg.sender;
newEntity.data = _data;
entityMapping[msg.sender] = newEntity;
return true;
}
function updateEntity(address _inputAddress, uint _data) public returns(bool done){
entityMapping[_inputAddress].data = _data;
return true;
}
}
When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not?
When executing addEntity on the contract which is using the mapping then it consumes 41771 gas whereas the contract using arrays consumes 62585 gas which is a whole lot more. The difference is a lot…
This is because of how Solidity is storing arrays and mappings. How much processing it requires to do it.
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?
The array solution consumes more gas. I believe that the array solution is consuming more gas because it requires a lot more to go through an array than an mapping. The difference is that a mapping requires less effort than an array to process.
I am not exactly sure about the detail really but it has to be something in how an array and mapping are indexed in Solidity.
Mapping only:
pragma solidity 0.7.5;
contract StorageMapping{
struct Entity{
address entityAddress;
uint data;
}
mapping (address => Entity) public entities;
function addEntity(uint _data) public{
require(entities[msg.sender].entityAddress == 0x0000000000000000000000000000000000000000, "Already registered");
entities[msg.sender] = Entity({data:_data, entityAddress:msg.sender});
}
function updateEntity(uint _data) public {
require(entities[msg.sender].entityAddress == msg.sender, "Not found in registry");
entities[msg.sender].data = _data;
}
}
Array only:
pragma solidity 0.7.5;
contract StorageArray{
struct Entity{
address entityAddress;
uint data;
}
Entity[] public entities;
function addEntity(uint _data) public{
for(uint i = 0; i<entities.length; i++){
require(msg.sender != entities[i].entityAddress, "Already registered");
}
entities.push(Entity({data:_data, entityAddress:msg.sender}));
}
function updateEntity(uint _data) public {
for(uint i = 0; i<entities.length; i++){
if (entities[i].entityAddress == msg.sender){
entities[i].data = _data;
break;
}
}
}
}
Mapping:
addEntity execution cost:
updateEntity cost (5th address):
6324
Array:
addEntity execution cost:
updateEntity cost (5th address):
19495
Array design consumes more gas when executing addEntity function as well as updateEntity function. With array we need to iterate the whole array until we find the entity we want to update. As the array grows the execution cost should be higher.
addEntity cost for mapping stayed consistent. With mapping we can use the address as a key to find the entity. There is no iteration involved.
Code:
Mapping
pragma solidity 0.8.0;
contract EntityMapping {
struct Entity {
uint data;
bool isEntity;
}
mapping (address => Entity) entities;
function addEntity(uint _data) public {
Entity memory newEntity;
newEntity.data = _data;
newEntity.isEntity = true;
entities[msg.sender] = newEntity;
}
function updateEntity(uint _data) public {
require(entities[msg.sender].isEntity == true);
entities[msg.sender].data = _data;
}
}
Array:
pragma solidity 0.8.0;
contract EntityArray {
struct Entity {
uint data;
address _address;
}
Entity[] entities;
function addEntity(uint _data) public {
Entity memory newEntity;
newEntity._address = msg.sender;
newEntity.data = _data;
entities.push(newEntity);
}
function updateEntity(uint _data) public {
uint i;
for (i = 0; i <= entities.length; i++ ) {
if (entities[i]._address == msg.sender) {
entities[i].data = _data;
break;
}
}
}
}
The Gas costs for each were as such:
Mapping:
addEntity:
execution cost 41551 gas
updateEntity (1st):
execution cost 6481 gas
updateEntity (6th):
execution cost 6481 gas
Array:
addEntity:
execution cost 62386 gas
updateEntity (1st):
execution cost 8934 gas
updateEntity (6th)
execution cost 22704 gas
The costs for adding and updating using arrays is more expensive because the underlying operations cost more gas to execute. The difference in cost for adding the 6th element in the array is due to the executing the loop calculation to find the msg.sender address, which is not necessary for the mapping.
In conclusion arrays requiring loops (or algorithms) to search should be avoided for large data sets if possible as this would consume quite a bit of unnecessary gas.
solution with an array:
pragma solidity 0.8.0;
// Define the contract
contract Arraycontract{
// Define the struct.
struct Entity{
uint data;
address _address;
}
// Define entity array
Entity[] entityStructs;
// Make function to add entity's
function addEntity(address _address, uint data ) public returns(bool success) {
// Declare empty entity.
Entity memory newEntity;
// set address
newEntity._address = _address;
// set balance of address
newEntity.data = data;
// push all the data to the struct
entityStructs.push (newEntity);
return true;
}
// Make function to update entity's
function updateEntity(uint _index, uint data) public returns(bool succes){
// Assign the provided data
entityStructs[_index].data = data;
return true;
}
}
solution with a mapping:
pragma solidity 0.8.0;
// Define contract
contract simpleMapping{
// Define struct
struct Entity{
uint data;
address _address;
}
// Define mapping entities
mapping (address => Entity) entityStruct;
// Create function to add data of an address
function addEntity (uint data) public returns(bool succes) {
// Declaire empty entity
Entity memory newEntity;
// Set balance of address
newEntity.data = data;
// Set address (sender is address)
newEntity._address = msg.sender;
// Create the struct.
entityStruct[msg.sender] = newEntity;
return true;
}
// Create function to update an entity
function updatEntity (address _address, uint data) public returns(bool succes){
// Assign approved data.
entityStruct[_address].data = data;
return true;
}
}
1. When executing the addEntity function, which design consumes the most gas (execution cost)?
Array: 62.830.
Mapping: 41.749.
2. Is it a significant difference? Why/why not?
This is quite a big difference with the mapping operation you save 21.081 on gas costs. you save about 30% per action you wanna do.
3. Add 5 Entities into storage using the addEntity function and 5 different addresses. update data and do on both contracts. Wich solution consumes more gas and why?
With the array solution it costs in total: 239.150 to add 5 different addresses to the storage.
And it costs: 6.670 gas to update an array.
With the mapping solution it costs in total: 208.745 gas to add 5 different addresses to the storage this is a difference of 30.405 in gas costs this is a significant difference.
And to update an array with the mapping solution it costs: 5.940 gas to update the array wich is a difference of: 730 in gas.
The array solution spents more gas because it has more process to perform. Because it has to loop through the whole array to find an address and then to update it. While with a mapping you can call a certain address and update it almost directly.
Hi, this is my solution:
pragma solidity 0.8.0;
contract assignmentMapping {
struct Entity{
uint data;
address _address;
}
mapping(address => Entity) public entities;
function addEntity(uint _data) public {
// require(entities[msg.sender]._address == address(0), "Entity already exists");
entities[msg.sender].data = _data;
entities[msg.sender]._address = msg.sender;
}
function updateEntity(uint _data) public {
// require(entities[msg.sender]._address != address(0), "Entity does not exist");
entities[msg.sender].data = _data;
}
}
contract assignmentArray {
struct Entity{
uint data;
address _address;
}
Entity[] public entities;
// mapping(address => bool) public isEntity;
function addEntity(uint _data) public {
// require(isEntity[msg.sender] == false, "Entity alrady exists");
entities.push(Entity(_data, msg.sender));
// isEntity[msg.sender] = true;
}
function updateEntity(uint _data) public {
// require(isEntity[msg.sender] == true, "Entity does not exist");
for(uint i=0;i<entities.length;i++) {
if (entities[i]._address == msg.sender) {
entities[i].data = _data;
break;
}
}
}
}
In this case the difference is overall small. We can see that first add into array costs 62286 (which is quite big cost difference), but every following add costs only 47286, which is cost similar to mapping (41476). The interesting question though is why array addEntity costs more on the first entry?
Array design costs much more (almost 4x), because it has to loop through all the array until it finds the entry with given address. On contrary mapping finds the specific value for address right away. However we can optimise looping through array with break statement, which stops after the address is found and can save us some cost, but it is still more expensive than mapping.
My code for the assignment.
I did include an isEntity function since I would imagine one would want to check if the entity exists so you don’t get duplicate entries or try to update a non-existant entity.
pragma solidity 0.7.5;
contract storageDesignMapping{
struct Entity{
uint data;
address _address;
bool isEntity;
}
mapping (address=> Entity) entityList;
function isEntity(address _entityAddress) private view returns (bool success){
return entityList[_entityAddress].isEntity;
}
//addEntity(). Creates a new entity for msg.sender and adds it to the mapping/array.
function addEntity(uint _entityData) public returns (bool success){
require (!isEntity(msg.sender),"Duplicate: this entity exists already.");
entityList[msg.sender].data=_entityData;
entityList[msg.sender]._address=msg.sender;
entityList[msg.sender].isEntity=true;
return true;
}
//updateEntity(). Updates the data in a saved entity for msg.sender
function updateEntity(uint _entityData) public returns (bool success){
require (isEntity(msg.sender),"Not found: this entity does not exist.");
entityList[msg.sender].data= _entityData;
return true;
}
}
pragma solidity 0.7.5;
contract storageDesignArray{
struct Entity{
uint data;
address _address;
}
Entity[] private entityList;
function isEntity(address _entityAddress) private view returns (bool success){
if (entityList.length==0) return false;
for(uint i = 0; i<entityList.length ; i++){
if (entityList[i]._address== _entityAddress){
return true;
}
}
return false;
}
//addEntity(). Creates a new entity for msg.sender and adds it to the mapping/array.
function addEntity(uint _entityData) public returns(bool success){
require (!isEntity(msg.sender),"Duplicate: this entity exists already.");
Entity memory newEntity;
newEntity.data = _entityData;
newEntity._address=msg.sender;
entityList.push(newEntity);
return true;
}
//updateEntity(). Updates the data in a saved entity for msg.sender
function updateEntity(uint _entityData) public returns(bool success){
require (isEntity(msg.sender), "Not found: this entity does not exist.");
for (uint i=0; i<entityList.length; i++){
if (entityList[i]._address==msg.sender){
entityList[i].data=_entityData;
return true;
}
}
}
}
When executing the addEntity function, which design consumes the most gas (execution cost)? Is it a significant difference? Why/why not? Add 5 Entities into storage using the addEntity function and 5 different addresses
mapping execution costs 44210 gas every time
array execution costs 63190 , 51655, 54290, 56925, 59560
Array costs way more from the start. I take it that the first entry in the array costs more since it has to create an actual array. The next actions start out lower since the array now exists.
With array it keeps going up since I added an isEntity function that loops through the array. I consider this function as quite elemental since you wouldn’t want duplicate entries into any database/array. The cost will keep increasing since it takes more actions to go through an ever expanding array.
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?
I’ve also used the isEntity function for the update so it wouldn’t be possible to update a non-existant entity.
mapping update costs 6456
array update of the first element in tthe array costs 8173, update of the fifth element costs 29249
It has to loop through the entire array in isEntity and then another loop to updateEntity so the further down the array the more actions will have to be performed. and the cost will go up.
Arrays cost much more since they always have to loop through. Not very advisable for large collections of data.
I didn’t get consistent results. In the end it looked like adding new entity to mapping was most affordable. Updating didn’t have huge differences and it was not consistent.
pragma solidity 0.8.1;
contract Mapping{
struct Entity{
uint data;
address _address;
}
mapping(address => Entity) store;
function addEntity() public{ // 2788 gas
store[msg.sender] = Entity(0, msg.sender);
}
function updateEntity(uint _data) public{ // 20515
store[msg.sender].data = _data;
}
}
contract Array{
struct Entity{
uint data;
address _address;
}
Entity[] store;
function addEntity() public{ // 27824
store.push(Entity(0, msg.sender));
}
function updateEntity(uint _data) public{ // 19965
for(uint i = 0; i < store.length; i++) {
if(store[i]._address == msg.sender){
store[i].data = _data;
break;
}
}
}
}
I tried two different array implementations, one that checks for existence and one that doesn’t.
In both cases it’s cheaper to add and update with a mapping than an array. I’m not sure why pushing to an array is more expensive than inserting into a mapping, but the update is more expensive for arrays because it requires a linear lookup rather than a constant time map lookup.
EntityArray (checks for existence in addEntity):
addEntity
1: tx cost: 84843, exec cost: 63379
2: tx cost: 73501, exec cost: 52037
3: tx cost: 77159, exec cost: 55695
4: tx cost: 80817, exec cost: 59353
5: tx cost: 84475, exec cost: 63011
Increases by 3658 each time.
updateEntity
5: tx cost: 40813, exec cost: 19349
EntityArray2 (doesn’t check for existence in addEntity):
addEntity
1: tx cost: 83962, exec cost: 62498
2: tx cost: 68962, exec cost: 47498
3: tx cost: 68962, exec cost: 47498
4: tx cost: 68962, exec cost: 47498
5: tx cost: 68962, exec cost: 47498
updateEntity
5: tx cost: 40813, exec cost: 19349
EntityMapping
addEntity
1: tx cost: 63113, exec cost: 41649
2: tx cost: 63113, exec cost: 41649
3: tx cost: 63113, exec cost: 41649
4: tx cost: 63113, exec cost: 41649
5: tx cost: 63113, exec cost: 41649
updateEntity
5: tx cost: 27181, exec cost: 5717
pragma solidity 0.8.0;
contract EntityArray {
struct Entity {
uint data;
address _address;
}
Entity[] entityArray;
function addEntity(uint _data) public returns (bool) {
bool exists = false;
for (uint i = 0; i < entityArray.length; i++) {
Entity memory entity = entityArray[i];
if (entity._address == msg.sender) {
exists = true;
}
}
if (!exists) {
Entity memory entity = Entity(_data, msg.sender);
entityArray.push(entity);
return true;
}
return false;
}
function updateEntity(uint _data) public returns (bool) {
for (uint i = 0; i < entityArray.length; i++) {
Entity storage entity = entityArray[i];
if (entity._address == msg.sender) {
entity.data = _data;
return true;
}
}
return false;
}
}
contract EntityArray2 {
struct Entity {
uint data;
address _address;
}
Entity[] entityArray;
function addEntity(uint _data) public returns (bool) {
Entity memory entity = Entity(_data, msg.sender);
entityArray.push(entity);
return true;
}
function updateEntity(uint _data) public returns (bool) {
for (uint i = 0; i < entityArray.length; i++) {
Entity storage entity = entityArray[i];
if (entity._address == msg.sender) {
entity.data = _data;
return true;
}
}
return false;
}
}
contract EntityMapping {
struct Entity {
uint data;
address _address;
}
mapping(address => Entity) entityMapping;
function addEntity(uint _data) public returns (bool) {
entityMapping[msg.sender] = Entity(_data, msg.sender);
return true;
}
function updateEntity(uint _data) public returns (bool) {
entityMapping[msg.sender].data = _data;
return true;
}
}