지난 글에 이어 시세에 따라 변하는 NFT를 직접 민팅해보고 bull, bear 상황에 맞게(+랜덤하게) 바뀌도록 코드를 수정하려고 한다. 블록체인 상에서 랜덤 숫자를 만들어내기 어려운 관계로 체인링크의 VRF
를 이용할 것이다.
https://vrf.chain.link/ 에서 지갑을 연결하고 Create Subscription
버튼을 눌러 vrf를 구독한다. 지난번에 faucet으로 받았던 LINK 토큰을 fund에 넣고, 이후 subscription ID를 잘 보관해놓는다.
지난번에 만들었던 priceFeed 컨트랙트에 VRF를 추가해보자. 원래는 한 컨트랙트에 모두 추가하려고 했는데 컨트랙트가 너무 길어져서 두 개로 분리했다. 하나는 체인링크의 Get a Random Number 예시 컨트랙트를 사용했고, 나머지 하나는 1편에서 만들었던 priceFeed 컨트랙트에서 VRF 값을 불러올 수 있도록 했다. 먼저 VRF 컨트랙트를 살펴보자.
VRF를 사용하기 위해선 vrfCoordinator
와 keyHash
값이 필요하다. Supported Networks 탭에서 값을 확인하고 각 네트워크에 맞춰서 할당 해주면 된다. Goerli testnet의 경우 최소 컨펌횟수는 3번이기 때문에 3 이상으로 입력해야 하고, 랜덤 숫자는 500개까지 요청할 수 있다.
constructor 부분에 subscribtionId
를 파라미터로 넣게 되어있다. 앞서 보관했던 subscribtionId
를 디플로이할 때 넣어주면 된다. VRF는 크게 2가지 함수로 이루어져있다.
requestRandomWords
함수가 실행되면 keyHash, s_subscriptionId 등의 변수 값을 넣어줘서 새로운 랜덤 숫자를 체인링크 VRF에 요청한다.
fulfillRandomWords
는 체인링크 VRF에서 만든 랜덤 숫자를 가져와서 s_randomWords에 저장한다.
그리고 마지막으로 민팅 컨트랙트에서 s_randomWords에 저장된 숫자를 받을 수 있도록 getRandomWords
함수를 만들어준다.
이제 컴파일 한 다음, subscriptionId
값을 넣어주고 디플로이 하면 된다. 이 때 Remix 왼쪽 상단에 있는 ENVIRONMENT를 Remix VM이 아닌 Injected Provider
로 설정해줘야 한다. 그래야 메타마스크에서 컨펌해줄 수 있다.
다시 체인링크 VRF 페이지로 가서 우리가 디플로이한 VRF 컨트랙트를 consumer
로 등록해줘야 한다. Add consumer 버튼을 눌러 등록해주자.
지난번에 만들었던 민팅 컨트랙트에 VRF 컨트랙트를 호출할 수 있도록 수정해주자. 변경된 부분만 살펴보면
먼저 VRF 컨트랙트를 임포트 해오고 민팅 컨트랙트 안에서 vrfContract 변수를 선언해준다. 이름은 아무렇게나 해도 상관없지만 맨 앞에 VRF 컨트랙트의 이름을 써줘야 한다.
constructor에 VRF 컨트랙트 주소를 받을 수 있도록 파라미터를 추가하고, 받아온 주소값을 위에서 선언한 vrfContract
변수에 넣어준다. 이렇게 주소값을 넣어줘야 VRF 컨트랙트를 호출할 수 있는 객체를 만들 수 있다.
가격이 변동되면 performUpkeep 함수가 실행되는데, 이 때 VRF 컨트랙트에 있는 requestRandomWords
함수를 호출해서 랜덤 숫자를 생성하도록 한다.
이후 tokenUri를 업데이트 할 때 VRF 컨트랙트의 getRandomWords
함수를 호출해서 생성된 랜덤 숫자를 가져온다. 랜덤 숫자는 엄청 큰 숫자일 수 있기 때문에 bearUrisIpfs
배열의 길이로 나눈 나머지를 이용해 배열 안에 있는 uri를 랜덤으로 선택할 수 있도록 했다.
이제 업데이트 주기
, priceFeed
, 앞서 디플로이했던 VRF 컨트랙트 주소
를 넣어주고 디플로이 한다. 마지막으로 메타마스크 주소를 넣어줘서 safeMint
함수를 호출하면 민팅 완료!
처음 VRF 예시 컨트랙트를 디플로이 할 때 디플로이 환경을 Remix VM
으로 설정했었다. 계속 디플로이가 안 되서 공식문서를 다시 살펴보니 Injected Provider Environment
로 디플로이 했어야 했다. 내 멋대로 하는 것이 아니라 공식 문서를 제대로 보는 것이 매우 중요하다는 것을 알았다.
민팅 컨트랙트에서 VRF 컨트랙트를 임포트 해올 때 계속 VRF 컨트랙트를 찾을 수 없다고 나왔었다. 왜 안되는지 구글링을 해본 결과, VRF 컨트랙트에서 설정한 컨트랙트의 이름과 민팅 컨트랙트에서 선언한 타입의 이름이 달랐었다. 밑의 사진은 동일한 이름으로 나오지만 오류가 났을 때는 contract VRFv2Consumer is VRFConsumerBaseV2 {}
로 되어있었다. 솔리디티 파일 이름만 바꾼 채로 임포트 해오려고 했던 것. 앞으로는 이름 때문에 삽질하지 않도록 주의하자.
민팅 컨트랙트를 디플로이할 때 계속 transaction fail
이 났었다. VRF 컨트랙트에 LINK 토큰도 보내보기도 하고, gas limit도 올려보기도 하는 등 정확한 원인을 파악하지 못 했기 때문에 많은 시행착오를 겪었었다. 그런데 문제는 PriceFeed 였다. 민팅 컨트랙트를 디플로이 할 때는 PriceFeed 주소를 넣어줘야 한다. 체인링크에서는 토큰별로 다양한 주소를 지원한다.
여기서 문제가 됐던 것은 Goerli 테스트넷의 priceFeed가 아닌 메인넷의 priceFeed 주소를 계속 넣고 있었던 것
. 내가 생각해도 제일 어처구니 없는 실수다. 아니 이 정도면 실수가 아닐지도.. 결국 주소를 제대로 입력해서 디플로이도 성공적으로 했고 민팅도 잘 할 수 있었다.
막상 민팅은 했지만 글을 쓰는 현재 시점이 이더리움 머지 업데이트
가 얼마 남지 않았고, 이로인해 체인링크에서는 Rinkeby 테스트넷을 지원하지 않기 때문에 Goerli 테스트넷으로 진행했어야 했다. 하지만 Rarible
이나 OpenSea
의 테스트넷은 Rinkeby만 지원해서 결국 민팅한 NFT가 변하는 것을 실시간으로 확인 못 하는 상황..
Rinkeby 테스트넷은 2023년 2분기~3분기 쯤에 Sepolia 테스트넷으로 대체 될 예정이라고 한다. Goerli를 지원해주는 NFT 마켓플레이스가 나오거나 메인넷에서 민팅을 하면 되지만 메인넷은 비용적인 부담이 있다. 그래도 체인링크의 VRF 사용 경험과 민팅을 했다는 것에 의의를 두자!