Different Transfer functions in ERC20

Hi, I am trying to understand what the difference is between the transfer functions on line 115-117:

function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;

and on lines 208-216:

function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
1 Like

Ohhh, you know, for a standard ERC20 contract, there are two functions to transfer token:

transfer(to, amount)
transfer(from, to, amount)

so there will have some of the the same code, so in order to reuse these logic, _transfer(sender, recipient, amount) comes. And transfer() is a public function you can call, but for _transfer(), it is a internal function, so you can not access to it directly.
That is my opinion, if I understand wrongly, please correct me.

2 Likes

@Skyge hi and thanks. It looks like the second transfer function is the actual transfer function that updates the state. I am not sure what the first one does as it returns a bool; it looks like it is just whether or not there was a transfer to a specific address and amount returning True/False?

1 Like

I remember there are some explanation in the CHANGELOG.md. but I can not find it.
@abcoathup Please help.

1 Like

Hi @crypto_economics,

As @Skyge said (thanks as always @skyge :pray:) there are two transfer functions which return bool as defined in the EIP (see: https://eips.ethereum.org/EIPS/eip-20), transfer and transferFrom, which in the OpenZeppelin Contracts ERC20 implementation call a single internal _transfer function which does the actual work (and changes the state).

In OpenZeppelin Contracts v3.x hooks were added, so that you could hook into functionality such as transfer: https://docs.openzeppelin.com/contracts/3.x/extending-contracts#using-hooks

1 Like

@abcoathup thank you. So is my understand of the different transfers below correct?

  1. function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true;
    This just returns whether a transfer took place or not?

2. function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true;
This returns a bool on whether or the internal allowance transfer took place?

3. function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount);
This is an actual transfer which will update the state.

1 Like

Hi @crypto_economics,

Yes, that is correct. Though transferFrom also updates the allowance (reducing it by the amount transferred).

1 Like