Native Currency, Wrapped Token, ERC-20

DeFi, Token, ERC-20, WETH9

개요

NFT 등으로 취급되는 종류의 토큰을 제외하고, 기본적으로 EVM 호환 네트워크에서의 토큰은 크게 3개 종류로 분류할 수 있다.

Native Token

네이티브 토큰이라고 불리우는 것들은 ERC-20 표준의 토큰이 아니고, 네트워크의 화폐이다. 따라서 계약 주소도 존재하지 않는다. 제로 어드레스(0x0…)는 네이티브 토큰의 계약 주소가 아니며, 토큰의 발행 및 소각을 위한 명시적인 주소의 역할에 가깝다.

시초의 이더리움 네트워크를 보면, 네이티브 토큰인 ETH는 스마트 컨트랜트가 아닌 프로토콜의 일부 그 자체이다. 따라서 스마트 컨트랙트를 위한 별도의 계약 주소가 필요하지 않으며, 계정(EOA)에 잔액(state)이 직접 기록되었다.

따라서 balanceOf 함수를 통해 잔액을 조회할 때도, 조회할 토큰의 주소의 파라미터로(token) 아무것도 할당하지 않으면 네이티브 토큰의 보유량을 직접 조회할 수 있다. 이 네이티브 토큰을 EVM 블록체인 네트워크 위에서 다른 탈중항화 애플리케이션과 작용시키고자 한다면 Wrapped 토큰으로 변환하는 과정을 거쳐야 한다.

ERC-20 Token

스마트 컨트랙트로 배포된 토큰 중, ERC-20 표준 인터페이스로 배포된 것들을 일컫는다. 이더리움 네트워크와 모든 EVM 호환 체인에서, EOA 계정과 네이티브 토큰을 제외한 ERC-20 토큰, NFT, 유동성 풀 주소 등, CA 주소를 가진 모든 것들은 스마트 컨트랙트다. 스마트 컨트랙트는 이더리움 네트워크 위에 배포된 각각의 애플리케이션이라고 볼 수 있다.

ERC-20 토큰 또한 각각의 계약 주소가 존재한다. 이 의미는 이것은 ETH같은 네트워크의 화폐가 아닌 애플리케이션이라고 볼 수 있다는 것이다. 그러므로 당연히 ETH와 같은 네트워크의 화폐와 인터페이스도 다를 수 밖에 없을 것이다.

Approve라는 단계가 필요한 이유도 같다. 탈중앙화 애플리케이션과 상호 작용할 때, 탈중앙화 애플리케이션이 이더리움 네트워크에서 사용자의 데이터에 대해 쓰기 권한이 필요하기 때문이다.

Wrapped Token

Wrapped Token은 EVM 블록체인 네트워크 위에서 토큰이라고 불리우는 것들처럼, 네이티브 토큰을 ERC-20 표준에 맞춘 것이라고 볼 수 있다. ETH라는 화폐를 스마트 컨트랙트 애플리케이션과 상호 작용하고, 다루기 위한 토큰의 형태로 바꾼 것이다.

그러므로 당연히 Wrapped 토큰의 경우에도 Approve 과정에 대한 사용자의 승인이 필요하다.

네이티브 토큰, Wrapped 토큰 → ERC-20 토큰

컨트랙트를 호출할 때, viem, wagmiwriteContract 메서드를 이용해서 작성하곤 한다. 여기서 많은 것이 동일하지만, 파라미터의 미묘한 차이가 있다.

Wrapped 토큰 → ERC-20 토큰

Wrapped 토큰에서 ERC-20 토큰으로의 스왑은 exactInputSingle, exactInput(멀티홉) 컨트랙트 메서드를 호출해서 이루어진다. Wrapped 토큰은 네이티브 토큰을 ERC-20 표준 인터페이스를 갖도록 만든 것이므로, Approve 과정에 문제가 없다면 다른 ERC-20 토큰들과의 스왑 과정과 다를 것이 하나도 없다.

네이티브 → ERC-20

네이티브 토큰에서 ERC-20 토큰으로의 스왑도 exactInputSingle, exactInput(멀티홉) 컨트랙트 메서드를 호출해서 이루어진다. 또한, 네이티브 토큰은 스마트 컨트랙트가 아니므로, 다른 ERC-20 토큰들의 스왑 과정에 존재하는 Approve 과정이 존재하지 않는다.

다만, 네이티브 토큰은 ERC-20 인터페이스를 지니지 않았으므로 Wrapping 과정이 필요한데 이것은 보통 deposit 이라는 컨트랙트 메서드를 호출해서 이루어진다. Uniswap 등의 라우터는 exactInputSingle 메서드를 호출하면 내부적으로 잔액을 보고 자동적으로 이 과정을 알아서 처리해준다(cf. uniswap/v3-periphery). 따라서 프론트엔드 애플리케이션에서 컨트랙트 메서드를 위한 파라미터인 argstokenIn 주소에 WETH 주소를 매핑해주면 된다.

또 하나 큰 차이점이 있다. value 필드의 값이다. 일반적인 ERC-20 토큰 간의 스왑을 진행하는 경우에는 사용할 일이 없다. 다만 ABI를 보면 확인할 수 있는 부분이 하나 있는데,

{
	// ...
	'stateMutability': 'payable',
}

여기서 state는 곧 네트워크의 잔액이고, 이더리움 네트워크라면 ETH가 될 것이다. 즉, 이 상태 변이성은 네트워크의 잔액이 변화할 수 있음을 묻는 것이다. payable, nonpayable이 있는데, ERC-20 토큰 간의 스왑은 네트워크에서 자동적으로 부과하는 가스비를 제외하면 지불할 것이 없다.

하지만 네이티브 토큰에서 출발할 경우, 아무리 라우터에서 자동적으로 Wrapping을 진행해준다고 해도 그 양에 대한 전달은 필요할 것이다. 다만, ERC-20 토큰이 아니므로 토큰의 양이라고 칭하기는 어려울 것이고, 네이티브 토큰의 지불로 구분해서 보낼 필요가 있을 것이다. 이를 위한 필드가 value인 것이다.

여기서 만약, 네이티브 토큰으로 출발하지만 payable을 위한 value에 그 어떤 값도 할당하지 않는다면 어떻게 될까? STF(SafeTransactionFrom failed) 오류가 발생할 확률이 큰데, 아마 가스비만 소진되고 트랜잭션은 반려되어 스왑에 실패할 것이다. value 없이 호출하면 라우터에 네이티브 토큰이 전달되지 않아서, 네이티브 토큰을 Wrapped 토큰으로 변환할 수 없고, 결국 풀에 토큰을 전송할 수 없어 스왑이 실패하게 된다.

트랜잭션 실행 단계:
1. ✓ 트랜잭션 검증     → 가스 소모
2. ✓ 함수 진입         → 가스 소모
3. ✓ 파라미터 디코딩   → 가스 소모
4. ✓ 조건문 실행       → 가스 소모
5. ✗ Require 실패     → Revert (1-4의 가스는 환불 안됨)

// Value는 ETH를 컨트랙트의 "금고"에 입금하는 것
// Value 없으면 금고가 비어있음 → 실패
트랜잭션 { value: 1 ETH }
    ↓
[SwapRouter 금고에 1 ETH 입금]
    ↓
msg.value = 1 ETH (컨트랙트가 사용 가능)
    ↓
WETH.deposit{value: 1 ETH}() (ETH → WETH 변환)
    ↓
Pool에 WETH 전송
    ↓
USDC 받기

WETH9

이어서 자연스럽게 생각하게 될 수 있는 게, 이 deposit, withdraw 메서드도 스왑 라우터 컨트랙트(ex. Uniswap V3)에 존재하는가에 대한 의문인데, 눈치 챘을 수도 있겠지만 그렇지 않다.

우선 Uniswap V3 같은 현대적인 스왑 라우터는 대개 다음과 같은 설계 철학을 따른다.

  1. 핵심 로직은 ERC-20 표준에만 집중한다. 라우터의 가장 복잡한 부분인 최적 경로 탐색, 유동성 풀과의 상호 작용, 가격 계산 등의 핵심 로직은 ERC-20이라는 단일 표준을 기반으로 설계되어 있다. (따라서 네이티브, ERC-20 등의 형태에 따른 복잡한 분기 처리도 걷어낼 수 있다.)
  2. 네이티브 토큰은 어댑터를 통해 처리한다. 네이티브 토큰은 ERC-20 표준을 따르지 않는다. 라우터는 이 예외 케이스를 직접 처리하지 않고, WETH9이라는 검증된 어댑터와 같은 방식으로 네이티브 토큰을 ERC-20 표준에 맞게 변환시켜 사용한다. (정규화의 과정으로도 볼 수 있다.)

쉽게 말하면, 스왑 라우터는 온전히 ERC-20 표준의 지원에 집중하고, 네이티브 토큰의 상호작용은 내부적으로 WETH9 컨트랙트를 이용해서 소화한다고 볼 수 있다. 실제로 Uniswap V3 periphery 계층의 코드를 보면 WETH9 컨트랙트를 호출하는 것을 확인할 수 있다. (cf. https://github.com/Uniswap/v3-periphery/blob/06823871/contracts/base/PeripheryPayments.sol)

그렇다면 WETH9 컨트랙트는 뭘까? 이더리움의 네이티브 토큰인 ETH는 ERC-20 표준이 생기기 전에 만들어졌다. 따라서 당시 DEX, DeFi 프로토콜에서 다른 ERC-20 토큰과 직접 상호작용하기 어려웠고, 이 문제를 해결하기 위해 ETH를 ERC-20 토큰과 호환되는 토큰으로 포장하는(Wrapping) 개념이 등장했다.

초기에는 0x Protocol, MakerDAO 등 여러 프로젝트가 각자의 필요에 따른 WETH 컨트랙트를 개발하고 배포했었고, 이로 인해 여러 벌의 WETH 컨트랙트가 존재하게 되었다. 이후 Dapphub라는 그룹에서 WETH9 버전을 배포했는데, 이 버전이 당시 문제점들을 개선하고, 안정성과 효율성이 높아 주요 DeFi 프로토콜들에서 채택되기 시작했고, 점차 표준으로 굳어지게 된 것이다. (cf. WETH9.sol : https://raw.githubusercontent.com/gnosis/canonical-weth/master/contracts/WETH9.sol)

이후 비효율적인 패턴을 개선하기 위한 WETH10과 같은 시도들이 있었으나, 이미 광범위하게 WETH9를 기반으로 구축된 생태계로 인해 현재까지도 표준으로써 인식되고 있다.

즉, deposit, withdraw, transfer 같은, 네이티브 토큰을 ERC-20 토큰으로 취급하고 다루기 위한 메서드는 WETH9 컨트랙트에 존재하고, 이 WETH9 컨트랙트의 ABI에 포함되어 있는 것이다.