Hey @nventuro, thanks for the answer.
State variables are private
to both prevent the automatic getters that come with public
, and the setters
that come with internal
. We find that providing direct access to state variables is error prone, and prefer to instead expose better abstractions with internal
functions. For example, instead of exposing balances in ERC20, and leaving it up to the user to remember to also update the total supply and emit events when required, we provide the...
I was not aware of the setters
on internal
- good to know, I will give it some testing.
The reason why all external
functions are public
is actually not related to calldata
, but to inheritance and overrides. You can override an external
function, but due to restrictions in Solidity it is not possible to call the parent’s implementation from the override, which prevents lots of valid use cases. You can read more about this on this PR...
I see, so it's an extensibility problem. Also, kinda unrelated but, then Solidity is asking you to basically decide before-hand if you intend your code to be extensible, right ?
This specific PR refers to solidity 0.6.x
virtual
keyword right ?
If so, why versions prior to 3.0
of openzeppelin/contracts
like the ERC20 also contain public
functions ? perhaps because that's how it's defined in the standard ?
Do you find this information helpful?
Very helpful indeed.
any other topics we could offer clarification on?
Actually, I have been thinking about something for a while now, I was wondering which way would be "best".
Let's say that I have a contract, that is an extension of another, perhaps it's a token an it extends the ERC20
and adds a couple more functionalities .
For the sake of the discussion, let's also say we are using 0.5.x
because this problem is, in my view, solved by interface inheritance in 0.6.x
.
How would you proceed to define this interface ? Would you define it as just a IToken
that contains the extra functions ? or would you go ahead and also add all the IERC20
functionality in there ?
The reason I ask this, is because of the following:
If I have another contract that uses my IToken
I could say:
import "./Interfaces/IToken.sol";
Contract Foo {
...
}
vs
import ".Interfaces/IERC20.sol";
import ".Interfaces/IToken.sol";
Contract Foo {
...
}
Technically speaking, the are both the same, because the interface
is only used to generate the expected ABI and functon signature.
But for me, this could also be interpreted as the first case allowing to communicate with IToken
contracts, and the second allowing both ERC20
and IToken
- atleast at first glance.
Perhaps this is an irrelevant question and I'm just overthinking it hahaha.
Where do you think it belongs
As to where it should live...That's a very good question hahaha. Not gonna lie, my best guess it that some of this information should be provided by the Solidity documentation, but I think that might be too much to ask from a reference source.
What would I like? Well, I think that you guys already do a very good job at providing good practices and very helpful utilities and libraries as well as valuable code to learn from.
That said, if this document is to be made I think that it should strictly focus on the thought process. i.e instead of saying "the private
keyword for state variables is used because blablabla..." it should focus on the thought process that needs to happen when developing a smart contract, which is then tied to specific decissions you can make, for example regarding the visibility of your storage.
This is because, in my opinion, the rabbit hole of trying to justify every alternative is just too much, because it depends entirely on the context of the work - I think it would just be better to outline the thought process, give context and then tie it to the different design decisions that one can make.
Perhaps it can be best thought as a walk through for the developing context and how that affects the design decisions ?
Sorry for the long answer.
Hope I'm making some sense hahaha - Thx !