I try to specify uint type to uint16 instead of just doing uint or uint256 when dealing with time stamps but it says that I must use at least uint24. It works with uint24, but shouldn't uint16 be plenty enough?
3 Answers
Not really an answer to what you're asking, but...
Decreasing the size of a uint
to less that 256 bits will increase the associated gas costs, not decrease it like you might be expecting. It'll be best to leave it as a uint256
.
See: Why does uint8 cost more gas than uint256?
Edit:
Noting that timestamps are the number of seconds since the Unix epoch:
uint16
maximum value = 65536 -> 01/01/1970 @ 6:12pm (UTC)uint24
maximum value = 16777216 -> 07/14/1970 @ 4:20am (UTC)
So these values are too small.
Calling block.timestamp
(or now
) currently returns 1508618011
, which is 0b1011001111010111010111100011011
. This requires 31 bits. So you're looking at least at a uint32
, but more if you want to be future-proof.
-
What if we were talking about something that would be saved to storage? I assume it would be best practices to forexample use uint8 instead of uint256 for decimals, saving gas costs too.– NowsyMeCommented Oct 21, 2017 at 20:17
-
Yep, true for variables written to storage - writing smaller quantities is cheaper. You'll want to experiment depending on how you're using the timestamps. Commented Oct 21, 2017 at 20:31
-
Storage is written in blocks of 32 bytes. Using smaller values is not cheaper Commented Oct 21, 2017 at 20:45
-
6
uint32
is more than enough, in my opinion:2^32 - 1
equals to4294967295
, i.e.Sun Feb 07 2106
. None of us will probably be alive at that time, and I highly doubt Solidity will still be used to write distributed applications :)– skozinCommented Nov 23, 2017 at 2:42 -
1When storing in a struct, they are lined up on 32 byte blocks, so you can fit multiple uint32s in a struct and save on storage costs big time...– KevinCommented Mar 13, 2019 at 20:12
Other answers aren't helping to clearly assess when to use the default uint256
or a smaller uintN
. I'll give it a try.
There are two parts:
Can your code benefit from tight packing?
(Tested with solc
0.8.15)
Tight packing provides clear gains (gas savings) if your code is regularly writing the tightly packed variables to storage together. For example:
contract A {
uint128 tightPacked1;
uint128 tightPacked2;
function writeBoth(uint128 tp1, uint128 tp2) public {
tightPacked1 = tp1;
tightPacked2 = tp2;
}
}
But if your code isn't writing these variables together, there are no gains from tight packing. More, there are even losses compared to the uint256
version because of downscaling as mentioned by Richard Horrocks (observed approx 2100 gas usage increase per write for non-0 numbers). For example:
contract A {
uint128 tightPacked1;
uint128 tightPacked2;
function write1(uint128 tp1) public {
tightPacked1 = tp1;
}
function write2(uint128 tp1) public {
tightPacked2 = tp2;
}
}
Last, if doing very few writes to storage (for example only in the contract constructor), you have to compare tight packing saving with increased costs due to notably downscaling.
In any case, use a tool to test gas costs of your functions in different scenarios.
Which uintN
to use
If tight packing is a good idea, you can look into picking the smallest uintN
that does the job.
For a timestamp, uint32
can be enough (2106 max). But for time durations, you can go much lower (for example uint24
can do 6 months).
According to Solidity docs block.timestamp
is a uint
which is an alias for unit256
block.timestamp (uint): current block timestamp
So I personally will use the same in case Solidity still around in Year 33658 :)