Uniswap V2의 ERC20Permit과 같은 함수들에 대한 표준이 있어서, 한번 정리해본다.
ERC-20 20에 대한 성공 요인을 approve
와 transferFrom
이라고 하고 있다.
이를통해 msg.sender
를 추상화해서 (msg.sender
가 직접 콜하지 않아도 된다는 의미인듯) token이 transfer될 수 있다.
하지만 여전히 approve
의 경우에는 EOA가 직접 호출해야 하고, gas를 위해 ETH를 보유해야한다는 것을 단점으로 꼽고 있다.
이 EIP에서는 allowance
mapping을 수정할 수 있는 ERC20의 extension 함수인 permit
에 대해 소개한다.
Contract Wallet일 경우에는 이 EIP에서 꼽은 ERC20의 단점을 해결할 수 있으나, 소유자-wallet을 분리하는 UX의 단점으로 인해 이 EIP가 UX적으로 더 나은 대안임을 얘기하고 있다.
결국 ERC20 토큰은 이더리움 내에서 2차적인 토큰이므로, gas를 지불하기 위해 반드시 사용자가 ETH를 보유해야 한다.
하지만 Native Token인 ETH처럼 user가 ETH를 보유하지 않고도 ERC2토큰을 사용할 수 있게끔 하도록 하기 위해 제안되었다.
각 ERC-20 함수마다 _by_signature suffix를 붙일 수도 있지만 (예를 들면 transfer_by_signature
), 일반성을 위해 permit
함수만을 추가하여 다양한 조합으로 상황에 맞게 사용할 수 있다고 한다.
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external
function nonces(address owner) external view returns (uint)
function DOMAIN_SEPARATOR() external view returns (bytes32)
EIP-20에 추가적으로 위의 3가지 함수를 implement해야 한다.
permit(owner, spender, value, deadline, v, r, s);
과 같이 콜을 하면,
approval[owner][spender]
값을 value
로 설정한다.nonce[owner]
를 1
증가시킨다. (replay 방지)또한 다음과 같은 조건들이 있다
deadline
owner
!= address(0)
nonces[owner]
== nonce
참고로 DOMAIN_SEPERATOR의 경우에는 EIP-712를 따른다.
(EIP-712에 대해서는 이 글을 참조하면 된다.)
keccak256(abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
owner,
spender,
value,
nonce,
deadline))
))
EIP-712를 이해했다면 무리 없이 이해할 수 있다.
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes(version)),
chainid,
address(this)
));
DOMAIN_SEPARATOR
는 위와 같이 생성된다.
(Dapp에 따라 빼거나 아래에 추가 가능)
permit은 일반적으로 relayer
가 Permit을 제출하는 경우가 있다.
따라서 이를 보류할 것을 우려하면, deadline
을 가깝게 만들어, 유효 기간을 제한할 수 있다.
혹은 uint256(-1)
로 설정하여 사실상 만료되지 않는 Permit을 생성할 수도 있다.
ERC-191과 EIP-712에서도 다루었지만, Replay Attack에 대해 고려하여야 한다.
동일한 Signature를 다른 contract, 혹은 다른 chain에서 사용될 수 있지만 이는 DOMAIN_SEPARATOR
에서 막을 수 있다.
동일한 contract에 동일한 signature를 여러 번 제출하는 경우가 있을 수 있다.
이는 nonce[owner]
의 값이 제출되는 signature의 nonce
와 일치하는지 확인을 통해 유효성을 검사한다.
만일 relayer가 signature를 제출하지 않고 있다면, owner
가 해당 Permit을 제출해서 nonce값을 증가시켜버려서, relayer가 갖고 있는 signature가 더이상 유효하지 않게 만들 수도 있다.
ecrecover
는 verify에 실패했을 경우, address(0)
을 리턴한다.
(ecrecover
는 실패로 인한 revert
를 발생시키지 않는다.)
따라서, 이를 반드시 고려해야 한다.
하지만 가급적이면 Openzeppelin의 ECDSA 라이브러리를 사용하는 것이 대부분의 상황에서 권고된다.
https://github.com/soliditylabs/ERC20-Permit/blob/main/contracts/ERC20Permit.sol
EIP-2612에 대한 구현 예시는 이 링크를 참조하면 된다.