
Description
Description
.updateOffer()
updates both the offer.total
property and the offer.expiresAt
property of an offer
struct.
However, it writes each of these changes in two separate writes to storage.
We can save gas by making a single write to storage, rather than two.
Scenario
Every time that .updateOffer()
is called to raise the price of an offer, we waste gas by writing to storage twice rather than once.
Impact
Medium: It costs 5,000 gas for each write to storage. If we do a single write to storage (and pack the properties more efficiently as mentioned in Issue #45), we can save 5,000 gas every single time the function is run.
Reproduction
(1) In .updateOffer()
, we retrieve a pointer to the offer struct that we will update:
Offer storage offer = tokenIdToOffer[_tokenId];
(2) If msg.value is greater than zero, we update offer.total
. Since this is a pointer to storage that we are updating, this costs 5,000 gas, a very expensive operation gas-wise:
offer.total += uint128(msg.value);
(3) Only after that do we update a different property of the same struct in storage, again costing us another 5,000 gas:
offer.expiresAt = uint64(newExpiresAt);
Fix
Instead, we can make both changes locally, and then do a single write to storage. If we combine this with the better struct-packing from Issue #45, we will save 5,000 gas on every write. Change this:
uint256 newExpiresAt = now + globalDuration;
// Check if the caller wants to raise the offer as well
if (msg.value > 0) {
// Set the new price
offer.total += uint128(msg.value);
}
offer.expiresAt = uint64(newExpiresAt);
to this:
uint256 newExpiresAt = now + globalDuration;
Offer updatedOffer = offer;
// Check if the caller wants to raise the offer as well
if (msg.value > 0) {
// Set the new price
updatedOffer.total += uint128(msg.value);
}
updatedOffer.expiresAt = uint64(newExpiresAt);
offer = updatedOffer;
We would also need to reorder the properties of the Offer struct, as outlined in Issue #45.