Why is ERC20Permit better than approve/transferFrom?

Hi there, glad to know the OZ team rolled out a draft version of ERC20Permit. As I was researching on pancakeswap, I found this permit function for their PancakeERC20 token. I then began doing research on EIP-191, 712 and 2612. I have to say, after spending hours, I am still only staying on the surface.

Could anyone please explain in layman terms what this permit function does and why it is a better solution than approve-transferFrom interplay. I read that anyone can call this function, but really who gets to call this function in a concrete example? Thanks.

function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
    bytes32 digest = keccak256(
        abi.encodePacked(
            '\x19\x01',
            DOMAIN_SEPARATOR,
            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
        )
    );
    address recoveredAddress = ecrecover(digest, v, r, s);
    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
    _approve(owner, spender, value);
}
2 Likes

Hi @maxareo,

By not relying on IERC20.approve , the token holder account doesn’t need to send a transaction, and thus is not required to hold Ether at all.
_From: https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#ERC20Permit_

Essentially a token holder doesn't need to hold Ether or create transactions to spend tokens. Instead a token holder can sign to permit an allowance for a contract, so an application can create the transaction to perform an action requiring tokens.

The token holder needs to sign one message compared with creating two transactions requiring Ether. The application could pay for the transaction and charge the user in tokens.

3 Likes

Thanks for the great answer @abcoathup.

Do you have an example implementation of ERC20Permit? I am confused as the constructor doesn't take a symbol like ERC20. I would like to replace UniswapV2ERC20.sol with OpenZeppelin as the defacto implementation of EIP-712 and EIP-2612.

1 Like

Use both the ERC20 and the ERC20Permit constructor as below. You don't need to inherit the ERC20 constructor because this is already inherited in the ERC20Permit, but it does need to be invoked in the constructor.

pragma solidity ^0.8.0;

import {ERC20, ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";

contract Token is ERC20Permit {
    constructor () ERC20("Token", "Symbol") ERC20Permit("Token") public {
        _mint(msg.sender, 1000);
    }
}