a public function calls another internal or private function in solidity - solidity

What I see in several smart contracts, written with solidity, is that a public function is written whose job is just calling another function, which is private or internal.
Here is an example from erc20burnable.sol
In this function _burn is internal, but burn is public.
`
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
`
or here is another one in erc1155.sol
`
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155: caller is not token owner or approved"
);
_safeBatchTransferFrom(from, to, ids, amounts, data);
}
`
What is the benefit of this structure? why it is common in smart contracts?
Thanks.
One reason for this, I guess, is this way we will be able to override parents, or add modifiers, etc.

It's a common practice used in other OOP languages as well.
One of the reasons is code reusability. If the same snippet (e.g. decrease balance of one address, increase balance of other address, and emit an event) is used in multiple methods (e.g. both transfer() and transferFrom()), you can bundle them into one private function (e.g. _transfer()). And then call this private function from both public functions. When you need to make a change in the code logic, you'll be able to make it in just one place instead of having to search for multiple places and leaving some out by mistake.
Other common reason - you already answered it yourself. This approach allows you to allow the user to specify only some params - for example the amount. But the user cannot specify from which address are the tokens going to be burned - it's always from their address. Even though the private function _burn() allows to specify the burner, the user is not allowed to specify it.

Related

Why Openzeppelin ERC721 defines two 'safeTransferFrom'?

Source code:
/**
* #dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* #dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
Hi everyone.
While reading the Openzeppelin ERC-721 source code, I found that it defined two safeTransferFrom method with different implementations.
I am curious why it's made in this way. Could anyone help me with it?
Many thanks.
It follows the ERC-721 standard that also defines two functions with the same name - but with different input params. Generally in OOP, this is called function overloading.
As you can see in the OpenZeppelin implementation, when you call the function without the data param, it passes an empty value.
I can't speak for the authors of the standard, but to me it seems like a more developer friendly approach compared to having to explicitly pass the empty value, since Solidity doesn't allow specifying a default param value.

ERC20 totalSupply Best practice

Best Practice Advice:
Is it acceptable to override the totalSupply() of an ERCToken if you're using a different variable to hold some of the supply and not holding all the tokens in the totalSupply variable directly?
example:
...
uint _extraSupplyForGivingAway = 1e27; //decimal 1e18 * 1M just an example
function totalSupply() public view override returns(uint totalSupply){
totalSupply = super.totalSupply() + _extraSupplyForGivingAway);
return (totalSupply);
}
The total value of the contract is not only the _totalSupply, it's also the _totalSupply and the extra tokens.
Question: Does the community and/or exchanges find this acceptable or not?
There are two different issues here. One is, are you conforming to the EIP-20 (ERC-20) standard in a way that will be understood by the community-at-large? Another is, is this a reasonable implementation for your business logic?
The latter issue is out-of-scope here since you haven't really provided enough information. So I will address the first, which is what I believe you wanted to know.
The reason I spell this out is because you fixate on implementation details but ERC20 is an interface standard, so it doesn't by-and-large dictate how things ought to be implemented.
In the case of totalSupply, all the standard says is:
totalSupply
Returns the total token supply.
function totalSupply() public view returns (uint256)
If it's not clear what that means, the EIP does link to the OpenZeppelin contract as an example implementation, which has:
/**
* #dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
So as long as the total number of minted tokens is returned, you are fine. It doesn't matter if you internally have it computed as the sum of two other private variables. I do have some lingering doubts about your implementation from what you wrote, but as I said, that's out-of-scope :)
I hesitate to add this and possibly muddy the waters but.. "tokens in existence" is somewhat ambiguous. Sometimes people have their burn function do an actual transfer to the zero address (not just the event), effectively removing them from the circulating supply, and adjust the total supply accordingly. So their totalSupply will then return the number of tokens held only by non-zero addresses. Block explorers may or may not account for this. I would avoid doing this unless you absolutely know what you're doing.

How to call _mint() on erc721 without emit Transfer

I've read here that it is possible to mint 2^256 nfts in a single transaction. I've tried to achieve this by directly assigning _owners and _balances mappings but ofc these are private variables so i can't change them. I tried making an _mint() override but that also didn't work. How does this process work?
For simplification, let's do a 10k NFTs scenario.
It's not about invoking a single mint() function 10k times, rather than building your contract logic in a way that allows setting up a range of valid IDs.
Using the MFS part of IPFS, you can upload multiple files into a folder using the same directory ID and actual file names. Example:
https://ipfs.io/ipfs/<dir_id_abc>/1.json
https://ipfs.io/ipfs/<dir_id_abc>/2.json
https://ipfs.io/ipfs/<dir_id_abc>/3.json
etc...
These metadata files contain links to the images.
Your contract can then implement a custom function that shadows an authorized address as an owner of the NFT if both following conditions are met:
The ID is in a valid range (in our case 1-10k)
The NFT is not owned by anybody else (i.e. it's owned by the default address 0x0)
function _exists(uint256 tokenId) override internal view returns (bool) {
if (tokenId >= 1 && tokenId <= 10000) {
return true;
}
return super._exists(tokenId);
}
function ownerOf(uint256 tokenId) override public view returns (address) {
address owner = _owners[tokenId];
// The ID is in a valid range (in our case 1-10k)
// The NFT is not owned by anybody else (i.e. it's owned by the default address 0x0)
if (tokenId >= 1 && tokenId <= 10000 && owner == address(0x0)) {
// shadows an authorized address as an owner
return address(0x123);
}
return super.ownerOf(tokenId);
}
The tokenURI() function then validates the token existence (using the _exists() function) and returns the final URI concatenated from the base URI (https://ipfs.io/ipfs/<dir_id_abc>/), the ID, and the .json suffix.
Mind that this approach does not work on the OpenZeppelin implementation, as their _owners property is private and not readable from child contracts. But you can take this snippet as an inspiration for a custom implementation that allows simulating an arbitrary default owner of 10k (or even 2^256) tokens.
Tbh I don't know how that could be possible without paying ungodly amounts of gas. Why are you trying to mint that many tokens? Are you trying to get all the NFTs in a collection? If so, you'll have to pay the gas costs for every mint regardless.

When is it considered good design to directly set property values on an object without the use of a setter?

This may not be the best kind of question suited to stackoverflow, but I'm only after an answer that best describes why programmers sometimes don't use setters/getters for properties, e.g. in the case of property injection (DI).
Consider this example...
class Test
{
public propertyA;
protected propertyB;
public function setPropertyB(val)
{
// do some logic to validate 'val'
this.propertyB = val;
}
public function getPropertyB()
{
return this.propertyB;
}
}
Why would you choose the style of directly setting propertyA:
var Test = new Test();
Test.propertyA = 1;
Over the setter option for propertyB:
var Test = new Test();
Test.setPropertyB(1);
I always use the setter/getter approach, but I have seen some pretty established frameworks using the propertyA approach interspersed with the propertyB approach. What benefits do we have using this method?
Why you might not care about encapsulation:
You might be throwing away the project 15 minutes later.
You might have found getters/setters to be bottlenecks for your CPU-bound code, causing you to optimize for performance instead of design.
The instance field might be immutable and read-only, so there might be no danger in exposing it.
You're too lazy to write getters/setters.
You should use getters and setters because they allow you to control the interface to your objects.
For example, let's say I have a bank account class in a Java application:
class BankAccount {
private int balance;
BankAccount() {
balance = 0;
}
public void deposit(int amount) {
balance = balance + amount;
}
public void withdraw(int amount) {
balance = balance - amount;
}
}
When my software needs to alter a bank account's balance through deposits and withdrawals, it calls the appropriate methods.
Now, along comes some sneaky individual who manages to figure out that they can increase their bank balance by telling their internet banking software to withdraw negative amounts of money. I can fix this bug by adding a precondition to the withdraw method, and the bug goes away.
If the balance field was instead public, and a whole bunch of classes were just manipulating it's value arbitrarily, those classes would now need to be changed. If some of those external classes were written by third parties, then we're looking at a whole lot of pain to get the bug fixed.
Why would you use public fields? In the general case, you probably shouldn't. Some languages allow you to have a field scoped as public, then if you need to add a getter/setter later on you can do so without changing your object's interface (I believe C# does this, but correct me if I'm wrong).

Is this setter 'evil'

There's alot of talk about getters and setters being 'evil' and what not.
My question is: is the following setter evil? (rest of class omitted for brevity's sake)
int balance
public void deposit(int amount)
{
this.balance += amount;
}
This class is emulating an ATM. In the UK there are a few ATM's that lets you deposit as well as withdraw therefore this object needs a way of changing its state (the balance). Is this setter 'evil'?
Except for the fact that there is no handling of exceptional conditions, it looks like a perfectly good OO method - it's called what it does, and it does what you'd expect.
I don't believe that that is what is meant when people talk about getters and setters, because this is not simply setting a member to the given value.
I don't care for setters and getters, but mostly because I think of my "objects" as higher-level entities in the codebase. E.g. (IMO) it would be "more wrong" to do the operation outside of the class:
account.SetBalance(account.GetBalance() + depositAmount)
Instead, you've implemented higher-level functionality in your object; you make a deposit and let the object figure out the right way of dealing with it. This allows much more centralized handling of exceptional conditions than the getter/setter example I gave above.
Is that a trick question? I ask because the provided method isn't even a "setter" method. It's an operation, not a property. Setters and Getters are generally accessor methods for private variables (properties). So i guess the answer to your question is:
That's not a setter, but as a general method that performs an operation on an object, it's not evil at all.
For a class, there's nothing evil about setting a value via a setter, but that's more of a function than a direct setter. Yes, it sets the value of a property, but it does it via addition rather than replacing the previous value and the names don't line up.
A real 'setter' would look more like this:
int balance
private void setBalance(int amount)
{
this.balance = amount;
}
public void deposit(int amount)
{
setBalance(this.balance + amount);
}
For your specific ATM problem, though, I very much doubt that an ATM adds a deposit to your balance immediately. It likely needs to be collected and posted via a separate mechanism.
Personally, I would call that a method, not a setter. The stereotypical setter would be
public void deposit(int new_balance)
{
this.balance = new_balance;
}
All it does is give you direct access to the internals of the class, thus defeating any value gained by encapsulating them and restricting access. Which is why people don't like them.
Well you would want to check for negative amounts, a zero amount, etc... but give the requirement it is ok.
Follow this rule of thumb, every variable you make should be final unless it has to change and never make set methods for instance variables unless you really want them to be changed outside of the class.
Not necessarily; you mention that you want to emulate the behaviour of an ATM (cash machine). And you're concerned that ATMs let you deposit as well as withdraw. But those operations, the deposit and withdrawl, would have to be serialized. You need all of your actions to be atomic, so this sort of method is better than one where you try to do more things.
One issue that I see is that you are using an integral type when dealing with money. Not an issue if this is a fixed-point number but there is no indication that it is so.
IMO, the ATM should not have 'balance' as a field.
(additionally, your 'deposit' method is not a setter)
You should probably have an Account object with a 'balance' field and possibly a convenience method 'modifyBalance' on it that takes a positive value to increment or a negative value to decrement the balance.
Then your ATM methods would call 'modifyBalance' on the Account object when performing those types of transactions.
You can't tell whether a single method is evil or not, it depends on the context and who has access to the object.
If you have getters and setters for all fields and everybody and his dog have access to the object, then that is very bad, as there is essentially no encapsulation of the data.
If on the other hand you have setters only for the fields which need it and the object is only known to a select few other objects which need to communicate with it, then that would be quite OK.
That's not a setter. That's a normal method (or member function, or whatever).
A setter is a function that sets a given variable to a given value, and is usually a bad idea. A method is a function that performs a given class operation. It's meaningful in terms of the class.
If you have a weird data structure, you may not actually have a "balance" variable. No matter what your data structure, you're going to have to have a "deposit" function. There's part of the difference.
that's not a setter, its a normal method
even if it was a setter, it's not evil
this is an evil setter
int _balance = 0;
public int Balance()
{
get { return _balance; }
set { } //now that's evil!
}