[Ethereum] the proper pattern to use a temporary variable and ensure it gets stored

design-patternsmemorysoliditystorage

A contract has a state variable, which is complex. For the example, lets say it is a mapping of structs. Inside a method, I want to access that and store the changes. Like so:

contract FooManager {
  struct Foo {
    uint expiresAt;
    uint balance;
  }
  mapping(address => Foo) public fooIndex;

  public function claimFrom(address minter) public payable {
    require(fooIndex[minter].balance < amount);

    fooIndex[minter].balance += msg.value;
    fooIndex[minter].expiresAt += 1000;
  }
}

In this simple example, the repetition of fooIndex[minter].method is still manageable and readable. But with e.g. child mappings in, say the Foo struct, it quickly becomes tangled and ugly. fooIndex[minter].claimants[msg.sender].balance = msg.value are no exceptions there.

What is the common pattern to DRY this? I can create a memory-variable, like below, but that is not stored. The extra gas for allocating memory, may become an issue if this pattern is repeated, but is proably negligible in most cases.

  public function claimFrom(address minter) public payable {
    Foo memory thisFoo;
    thisFoo = fooIndex[minter];
    require(thisFoo.balance < amount);

    thisFoo.balance += msg.value;
    thisFoo.expiresAt += 1000;
  }
}

I've seen some examples in which, at the end, the memory-variable gets assigned back to the storage variable. Like so:

  public function claimFrom(address minter) public payable {
    Foo memory thisFoo;
    thisFoo = fooIndex[minter];
    require(thisFoo.balance < amount);

    thisFoo.balance += msg.value;
    thisFoo.expiresAt += 1000;

    fooIndex[minter] = thisFoo;
  }

It works, but seems clumsy to me: assigning the wrong thing, or forgetting to re-assign is an obvious mistake to make. It also seems wasteful to me, esp in the case of large structs, in which only the dirty (changed) attributes need to be overridden, and not the entire struct; again, this is more obvious in situations where a struct contains child-mappings with other structs and so on: a tree. I'm not sure, if the compiler can and will optimize this, though.

Did I miss an obvious language construct? Did I misunderstand the storage and memory completely?

Best Answer

The point is that it is cheaper to use memory variables for elaboration than storage variables.

The approach to use a temporary memory variable is efficient and clean, just make two calculation in order to understand if it is convenient in your particular case.

I mean: if you just add a value to the storage variable and that’s all, it can be evaluated, may be it is not the case; on the contrary in the case you access ten times the temporary variable and then store the value in the storage variable, there are no doubts.

Evaluate your needing: the more clean is the more convenient.

(You can find gas cost for accessing and creating temp variables in the yellow paper or elsewhere very easily)

Related Topic