1

If only transferFrom is called, an insufficient allowance error occurs. However, if you call this.transferFrom using this keyword, it transfers without error (with the permit method).

What difference does it make?

//SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";

contract MyContract is ERC20, ERC20Permit {
    uint public INITIAL_SUPPLY = 1000000000000000000000000;
    event wPermit(address indexed owner, address indexed spender, uint256 amount);
    constructor() ERC20("YYYYK", "YYYK") ERC20Permit("YYYYK_TEST") {
        _mint(msg.sender, INITIAL_SUPPLY);
    }
     
    function transferWithPermit_success
    (
        uint256 _amount,
        uint256 deadline, 
        uint8 v, 
        bytes32 r, 
        bytes32 s
    ) external {
        require(_amount > 0, "Cannot send 0");
        permit(msg.sender, address(this), _amount, deadline, v, r, s);
        this.transferFrom(msg.sender, address(this), _amount);
    }

    function transferWithPermit_fail
    (
        uint256 _amount,
        uint256 deadline, 
        uint8 v, 
        bytes32 r, 
        bytes32 s
    ) external {
        require(_amount > 0, "Cannot send 0");
        permit(msg.sender, address(this), _amount, deadline, v, r, s);
        transferFrom(msg.sender, address(this), _amount);
    }
}

2 Answers 2

2

This is a bit difficult to explain, let me try: When using this function:

permit(msg.sender, address(this), _amount, deadline, v, r, s);

You are granting permission to the contract address to use the caller's tokens. In other words, the contract instance "this" is the one that obtains the permission to execute the transaction.

The transferFrom() function of the ERC20 library is as follows:

function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}

If you observe these lines:

address spender = _msgSender();
_spendAllowance(from, spender, amount);

Here we can see that the function uses the permission of the "spender" to execute the transaction. And the spender is the "msg.sender".

Therefore, if we call transferFrom() directly, the function will look for the permission in the msg.sender, which in this case is the caller of the function. And this user does NOT have the permission, as permit granted it to the contract, not the caller.

On the other hand, if we call this.transferFrom(), we are indicating that the one calling the function is "this", that is, the current contract instance. And the contract does have the permission granted by the permit function.

This is why this.transferFrom() works, but transferFrom() does not.

I hope this is clear, if not, please do not hesitate to ask for clarification.

1
  • amazing, thanks!!
    – y2k2000
    Apr 5, 2023 at 3:18
2

The difference is when you use this.transferFrom(), the contract makes an "external" call to itself, which modifies msg.sender to itself. When you call transferfrom() the original msg.sender is preserved as it makes an internal call.

1
  • amazing, thanks!!
    – y2k2000
    Apr 5, 2023 at 3:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.