Why OZ uses safecast for block.timestamp?

In OpenZeppelin's code, we read:

SafeCast.toUint48(block.timestamp)

I wonder what's the reason of using safecast and why would it be wrong to write: uint48(block.timestamp).

I understand that if block.timestamp for some reason exceeds uint48, uint48(block.timestamp) would result in an underflow, causing dangerous effects.

My confusion is that in which blockchain will block.timestamp ever exceed uint48 ? max number of uint48 is a huge number and would take many,many years. Note that even though block.timestamp can be set by miners, it's still enforced in blockchains - i.e you can not set it by more than 30sec difference in ethereum for example. So is it worth to spend more gas for safecast rather than do uint48(block.timestamp) ? I'd understand OZ's approach if they wanted to do this for uint32 because uint32's max value in epoch will be year = 2106, but uint48 is hugeeee.

Would appreciate your inputs on this.

2 Likes

Hi @Giorgi_Lagidze,

As of now, this is only done in Time.sol and AccessControlDefaultAdminRules.sol. I would say it's not a controversial decision since we generally do SafeCast when there's a potential overflow unless we know an invariant (like ERC721Consecutive).

In this case, although I agree we can assume the block.timestamp never to overflow an uint48 (it'd take ~8 million years if I'm right), I am not sure we can safely assume every blockchain out there is going to expose the same timestamp value.

I'd keep it for the sake of ensuring the contract is safe even under unexpected timestamp values.

1 Like