Wyvern Protocol 이해하기
Wyvern은 first-order 분산 교환 프로토콜이다. OpenSea 같은 NFT 마켓플레이스의 핵심 거래 로직으로 사용되었던 프로토콜로, 이더리움 위에서 다양한 자산을 trustless하게 교환할 수 있게 해준다.
기본 개념
Wyvern에서 각 주문(order)은 두 개의 별개 자산 간 거래를 명시한다. 중요한 점은 주문 자체는 이더를 소모하지 않는다는 것이다. 주문은 오프체인에서 서명되어 relay 서버에 전송되고, 실제 교환이 일어날 때만 가스비가 발생한다.
일반적인 중앙화 거래소에서는 서버가 모든 주문을 관리하고 매칭한다. 반면 Wyvern은 주문 생성과 서명은 오프체인에서 하되, 실제 자산 이동은 스마트 컨트랙트가 처리한다. 이렇게 하면 주문을 올리고 취소하는 데 가스비가 들지 않으면서도 자산 교환의 신뢰성은 블록체인이 보장해준다.
Wyvern의 거래들은 상태 변화에 대한 술어(predicate)를 지정한다. 주문은 maker가 만든 호출, 상대방이 만든 호출, 그리고 주문 메타데이터를 부울에 매핑하는 함수다. 쉽게 말해 "이 조건이 맞으면 거래를 승인한다"는 규칙을 정의하는 것이다. 이러한 술어는 임의적이기 때문에 이더리움에서 표현할 수 있는 모든 자산 또는 자산의 조합을 Wyvern 주문으로 교환할 수 있다.
주요 용어
maker 는 주문의 생성자를 의미한다. NFT를 팔고 싶은 사람이 주문을 만들면 그 사람이 maker가 된다. maker는 "내 NFT를 100 USDC에 팔겠다"와 같은 주문을 생성하고 서명한다.
counter party 는 거래 상대방이다. maker가 만든 주문에 응하는 사람을 말한다. 위 예시에서 100 USDC를 내고 NFT를 사려는 사람이 counter party가 된다.
order 는 Wyvern에서의 주문으로, maker 혹은 counter party의 요청을 매핑하는 함수다. 주문에는 어떤 자산을 주고, 어떤 자산을 받을 것인지, 그리고 이 거래가 유효한 조건이 무엇인지가 담겨있다.
registry 는 각 사용자의 proxy 컨트랙트를 등록하고 관리하는 컨트랙트다. Wyvern Exchange는 이 registry를 조회해서 각 사용자의 proxy 주소를 확인하고, 해당 proxy가 정당한 것인지 검증한다.
proxy 는 사용자를 대신해서 자산을 전송하는 컨트랙트다. 사용자는 자신의 NFT나 토큰에 대한 전송 권한을 이 proxy에게 위임한다. 왜 직접 전송하지 않고 proxy를 쓸까? proxy를 사용하면 한 번 권한을 위임해두면 이후 여러 거래에서 반복적인 승인 없이 거래할 수 있다. 또한 Wyvern Exchange가 신뢰할 수 있는 proxy만 사용하도록 강제할 수 있어서 보안성도 높아진다.
calldata 는 주문 로직의 핵심 부분이다. 각 주문의 static callback(predicate function)은 호출의 인자, 상대방 호출, 주문 메타데이터(Ether value, timestamp, matching address)를 받아서 해당 주문을 매칭할 것인지 결정하고, 매칭되었다면 얼마나 채울 것인지 정한다.
Static Market 은 주문의 유효성을 검증하는 컨트랙트다. NFT→ERC20 거래와 ERC20→NFT 거래 각각에 대해 Static Market이 존재하고, 두 주문이 서로 매칭 가능한지 확인한다. 예를 들어 판매자가 "NFT #123을 100 USDC에 판다"고 했고, 구매자가 "NFT #123을 100 USDC에 산다"고 했다면 이 두 주문은 매칭 가능하다고 판단한다.
주문의 구성
Wyvern 주문(Order)은 다음과 같은 핵심 필드들로 구성된다.
- maker: 주문 생성자의 주소
- registry: 사용할 프록시 레지스트리 컨트랙트 주소
- staticTarget: 주문 유효성을 검증할 Static Market 컨트랙트 주소
- staticSelector: staticTarget에서 호출할 함수의 selector (함수 시그니처의 첫 4바이트)
- staticExtradata: staticTarget 함수에 전달할 추가 데이터 (가격, 토큰 주소 등)
이 중 staticTarget, staticSelector, staticExtradata 세 필드가 Wyvern의 핵심 설계를 보여준다. 거래 조건을 하드코딩하는 대신, 검증 로직을 외부 컨트랙트에 위임함으로써 다양한 거래 패턴을 유연하게 지원할 수 있다.
주문은 크게 두 가지를 체크한다.
첫째는 registry 검증이다. 주문자와 상대방이 유효한 레지스트리를 사용하는지 확인한다. 이를 통해 악의적인 컨트랙트가 거래에 끼어드는 것을 방지한다.
둘째는 calldata 구성이다. call 및 countercall을 구성하는 사전 준비가 주문 로직의 대부분을 차지한다. 각 주문의 static callback은 호출의 인자들을 받아 해당 주문의 매칭 여부와 fill 양을 결정한다. calldata에는 실제로 어떤 컨트랙트의 어떤 함수를 어떤 인자로 호출할지가 인코딩되어 있다.
Call과 Countercall
call 은 주문의 maker로부터 그들의 proxy를 통해 실행되는 최초의 호출이다. NFT 판매 주문이라면 call은 "NFT를 구매자에게 전송하라"는 명령이 된다.
countercall 은 거래 상대방으로부터 실행되는 두 번째 호출이다. 위 예시에서 countercall은 "ERC20 토큰을 판매자에게 전송하라"는 명령이 된다. 주문은 maximum fill에 대해 서명하고, static call은 uint를 반환하여 주문이 매칭될 경우 업데이트될 fill 값을 명시한다.
call과 countercall은 atomicMatch 함수 안에서 함께 실행된다. 둘 중 하나라도 실패하면 전체 트랜잭션이 롤백되므로, 한쪽만 자산을 보내고 다른 쪽은 안 보내는 상황이 발생하지 않는다.
주문의 승인
주문은 항상 maker address로부터 승인되어야 한다. maker address는 call을 수행하는 proxy 컨트랙트를 소유한다.
승인은 세 가지 형태로 진행될 수 있다.
signed message 방식은 가장 흔하게 쓰이는 방식이다. 오프체인 상에서 order hash에 서명하는 것이기에 주문 생성에 수수료가 들어가지 않는다. 사용자는 MetaMask 같은 지갑으로 주문 데이터에 서명만 하면 되고, 이 서명은 relay 서버에 저장된다. 나중에 매칭이 일어날 때 이 서명이 온체인에서 검증된다.
pre-approval 방식은 미리 온체인에서 주문을 승인해두는 방식이다. 트랜잭션을 보내서 "이 주문을 승인한다"고 컨트랙트에 기록해둔다. 가스비가 들지만 서명 없이도 주문이 유효해진다.
match-time approval 방식은 매칭 시점에 승인하는 방식이다. 구매자가 직접 atomicMatch 트랜잭션을 보내면서 자신의 주문을 함께 승인한다.
주문 교합(Matching) 흐름
실제 ERC721과 ERC20 간의 거래가 어떻게 이루어지는지 단계별로 살펴보자.

위 다이어그램에서 A는 NFT 판매자이고, B는 NFT 구매자로 ERC20으로 지불한다.
사전 준비 단계 (오프체인/온체인 혼합)
1단계: Proxy 생성 및 Registry 등록
A와 B 모두 자신의 ERC721 혹은 ERC20 자산의 대리인인 Proxy를 생성하고 Registry에 등록한다. 이 과정은 처음 한 번만 하면 되고, 이후 모든 거래에서 같은 proxy를 재사용한다. Proxy Registry 컨트랙트에 자신의 proxy가 등록되면, Wyvern Exchange가 이 proxy를 신뢰할 수 있게 된다.
2단계: NFT 전송 권한 위임
A는 자신의 Proxy에게 ERC721 자산에 대한 전송 권한을 부여한다. 이는 NFT 컨트랙트의 setApprovalForAll 함수를 호출해서 "내 proxy가 내 NFT를 전송할 수 있게 해줘"라고 설정하는 것이다. 다이어그램에서 A → A's proxy → NFT Contract 흐름이 이 부분이다.
3단계: ERC20 전송 권한 위임
B는 자신의 Proxy에게 ERC20 자산에 대한 전송 권한을 부여한다. ERC20 컨트랙트의 approve 함수를 호출해서 proxy가 토큰을 전송할 수 있게 한다. 다이어그램에서 B → B's proxy → ERC20 Contract 흐름이 이 부분이다.
주문 생성 단계 (오프체인)
4단계: 판매 주문 생성 및 서명
A는 주문정보를 생성하고 서명하여 relay 서버에게 주문을 전송한다. 주문에는 "NFT #123을 100 USDC에 판다"와 같은 정보가 담기고, A의 지갑으로 이 주문에 서명한다. 이 과정은 완전히 오프체인에서 이루어지므로 가스비가 들지 않는다.
5단계: 구매 주문 생성 및 서명
B도 주문정보를 생성하고 서명하여 relay 서버에게 주문을 전송한다. "NFT #123을 100 USDC에 산다"와 같은 주문을 만들고 서명한다. 마찬가지로 가스비가 들지 않는다.
매칭 및 실행 단계 (온체인)
6단계: 주문 검증
A와 B의 두 거래쌍이 교환 가능한지 확인하기 위해 Wyvern Exchange에게 두 개의 주문에 대한 교환을 요청한다. Wyvern Exchange는 각 주문의 staticTarget(Static Market)을 호출하여 두 개의 주문이 올바른지 검증한다. 다이어그램에서 NFT→ERC20 Static Market과 ERC20→NFT Static Market이 각각 판매 주문과 구매 주문을 검증한다.
7단계: atomicMatch 실행
검증이 완료되면 atomicMatch 함수가 실행된다. 이 함수 안에서 다음 일들이 원자적으로(atomic하게) 일어난다.
먼저 Wyvern Exchange는 Proxy Registry 컨트랙트를 확인하여 A와 B의 프록시 정보를 체크한다. 등록된 정당한 proxy인지 확인하는 것이다.
그 다음 각 프록시 컨트랙트에게 자산 전송을 요청한다. A의 proxy는 NFT Contract를 호출해서 NFT를 B에게 전송하고, B의 proxy는 ERC20 Contract를 호출해서 토큰을 A에게 전송한다.
8단계: 거래 완료
A와 B의 프록시 컨트랙트가 각 자산을 상대방에게 이동시키면 주문 교합이 종료된다. 모든 것이 하나의 트랜잭션 안에서 일어나므로, NFT는 갔는데 토큰은 안 오는 상황은 절대 발생하지 않는다.
핵심 포인트
Proxy 패턴의 장점
Wyvern의 핵심은 proxy 패턴에 있다. 사용자는 직접 자산을 전송하지 않고, 자신만의 proxy 컨트랙트를 통해 자산을 관리한다. 이 proxy는 registry에 등록되어 있어 Wyvern Exchange가 검증할 수 있다.
proxy 패턴을 사용하면 한 번 권한을 위임해두면 이후 거래마다 새로 승인할 필요가 없다. 또한 Wyvern Exchange는 registry에 등록된 proxy만 신뢰하므로, 악의적인 컨트랙트가 거래에 끼어드는 것을 방지할 수 있다.
오프체인 서명의 효율성
오프체인 서명을 활용하여 주문 생성 시 가스비가 들지 않는다는 점도 중요하다. 실제 자산 이동이 일어나는 매칭 시점에만 가스비가 발생하므로 효율적인 거래가 가능하다. 주문을 올렸다가 취소해도 가스비가 들지 않으니, 사용자 입장에서 부담 없이 주문을 관리할 수 있다.
Atomic 실행의 안전성
atomicMatch 함수 덕분에 거래의 안전성이 보장된다. call과 countercall이 하나의 트랜잭션 안에서 함께 실행되므로, 둘 중 하나라도 실패하면 전체가 롤백된다. 이로 인해 한쪽만 자산을 잃는 상황이 발생하지 않는다.