Hello @itinance, thank you for this question! This comes up often enough that I think it may warrant a guide of its own.
As a general rule, in OpenZeppelin we attempt to provide tools for you to be able to write correct code, but we don’t make (too many) assumptions on what it is you may want to write. Additionally, we do this ways that prevent you from making avoidable errors. For example, we won’t let you modify storage variables directly, since those usually go hand in hand with emitting events, updating some other variables, etc. (see in @frangio’s example above how updating _balances
in ERC20
requires a Transfer
event to be emitted).
However, for users to be able to write anything other than the most basic contracts, some added flexibility is required. That’s why we usually expose internal
functions (which always have a leading underscore, like _transferOwnership
) that have none of the usual access control, but still perform required safety checks,emit events, etc.: they are there for you to build on top of OpenZeppelin.
For example, you may want to provide a social recovery mechanism, where e.g. if 5 predetermined accounts (friends of yours) send a specific message, ownership of the contract is transferred to some other account (because you may have lost access to your own owner account). The only way to implement such a feature is via _transferOwnership
. Take a look also at this guide, which shows how different ERC20
supply mechanisms can be implemented by using _mint
.
Hope this helps clarify things!