ERC-2612: Permit Extension for EIP-20 Signed Approvals

frenchkebab·2023년 5월 10일
1

EIP / Open Source

목록 보기
4/9
post-thumbnail

Uniswap V2의 ERC20Permit과 같은 함수들에 대한 표준이 있어서, 한번 정리해본다.

Abstract

ERC-20 20에 대한 성공 요인을 approvetransferFrom이라고 하고 있다.

이를통해 msg.sender를 추상화해서 (msg.sender가 직접 콜하지 않아도 된다는 의미인듯) token이 transfer될 수 있다.

하지만 여전히 approve의 경우에는 EOA가 직접 호출해야 하고, gas를 위해 ETH를 보유해야한다는 것을 단점으로 꼽고 있다.

이 EIP에서는 allowance mapping을 수정할 수 있는 ERC20의 extension 함수인 permit에 대해 소개한다.

참고

Contract Wallet일 경우에는 이 EIP에서 꼽은 ERC20의 단점을 해결할 수 있으나, 소유자-wallet을 분리하는 UX의 단점으로 인해 이 EIP가 UX적으로 더 나은 대안임을 얘기하고 있다.

Motivation

결국 ERC20 토큰은 이더리움 내에서 2차적인 토큰이므로, gas를 지불하기 위해 반드시 사용자가 ETH를 보유해야 한다.

하지만 Native Token인 ETH처럼 user가 ETH를 보유하지 않고도 ERC2토큰을 사용할 수 있게끔 하도록 하기 위해 제안되었다.

각 ERC-20 함수마다 _by_signature suffix를 붙일 수도 있지만 (예를 들면 transfer_by_signature), 일반성을 위해 permit 함수만을 추가하여 다양한 조합으로 상황에 맞게 사용할 수 있다고 한다.

Specification

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 방지)

또한 다음과 같은 조건들이 있다

  1. current blocktime <= deadline
  2. owner != address(0)
  3. nonces[owner] == nonce

참고로 DOMAIN_SEPERATOR의 경우에는 EIP-712를 따른다.
(EIP-712에 대해서는 이 글을 참조하면 된다.)

서명할 message 내용

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

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에 따라 빼거나 아래에 추가 가능)

Rationale

deadline

permit은 일반적으로 relayer가 Permit을 제출하는 경우가 있다.
따라서 이를 보류할 것을 우려하면, deadline을 가깝게 만들어, 유효 기간을 제한할 수 있다.

혹은 uint256(-1)로 설정하여 사실상 만료되지 않는 Permit을 생성할 수도 있다.

Replay Attack

ERC-191EIP-712에서도 다루었지만, Replay Attack에 대해 고려하여야 한다.

Replay Attack Scenario 1

동일한 Signature를 다른 contract, 혹은 다른 chain에서 사용될 수 있지만 이는 DOMAIN_SEPARATOR에서 막을 수 있다.

Replay Attack Scenario 2

동일한 contract에 동일한 signature를 여러 번 제출하는 경우가 있을 수 있다.

이는 nonce[owner]의 값이 제출되는 signature의 nonce와 일치하는지 확인을 통해 유효성을 검사한다.

만일 relayer가 signature를 제출하지 않고 있다면, owner가 해당 Permit을 제출해서 nonce값을 증가시켜버려서, relayer가 갖고 있는 signature가 더이상 유효하지 않게 만들 수도 있다.

ecrecover 주의 사항

ecrecover는 verify에 실패했을 경우, address(0)을 리턴한다.
(ecrecover는 실패로 인한 revert를 발생시키지 않는다.)

따라서, 이를 반드시 고려해야 한다.

하지만 가급적이면 Openzeppelin의 ECDSA 라이브러리를 사용하는 것이 대부분의 상황에서 권고된다.

Example

https://github.com/soliditylabs/ERC20-Permit/blob/main/contracts/ERC20Permit.sol

EIP-2612에 대한 구현 예시는 이 링크를 참조하면 된다.

profile
Solidity에 대해 공부하고 있습니다.

0개의 댓글