Technology, Tutorials, for Developer

Klaytn에서 Chainsafe 와 Thirdweb을 이용해 Unity 게임을 토큰 게이팅하는 방법

목차

  • Background
  • Prerequisites
  • Getting Started
  • Downloading ChainSafe Gaming SDK
  • Initializing and setting up Unity project
  • Importing ChainSafe SDK into your project
  • ChainSafe Server Settings
  • Setting up WebLogin Prefab and Gaming Scene
  • Creating and deploying NFT smart contract using Thirdweb
  • Creating Game Objects
  • Creating C# Script on Unity
  • Connecting C# Script with game objects
  •  Testing and running game application
  • Conclusion

들어가며

토큰 게이팅은 특정 토큰(대체 가능 또는 대체 불가능) 보유자에게 독점 콘텐츠, 이벤트, 경험 등에 대한 액세스 권한을 부여하는 시스템입니다. 이러한 토큰은 독점의 세계에 접근하기 위한 열쇠라고 생각하면 됩니다.

토큰 게이팅은 블록체인과 스마트 컨트랙트의 불변성, 탈중앙화, 투명성을 활용하여 커뮤니티 구성원에게 독점적인 경험을 제공합니다. 즉, 회원이 토큰 게이팅된 콘텐츠나 경험에 액세스하려고 시도하면 시스템은 디지털 지갑을 확인하여 필요한 토큰이 있는지 확인합니다. 사용자의 지갑에 필요한 토큰이 있으면 액세스가 허용되고, 그렇지 않으면 액세스가 거부됩니다.

이 흥미로운 혁신은 독점권을 창출하고 충성도가 높은 커뮤니티 회원에게 보상을 제공하는 강력한 도구로, 회원 간의 강력한 공동체 의식을 조성합니다. 지난 몇 년 동안 토큰 게이팅은 다음과 같은 사용 사례에 적용되어 구현되었습니다.

  • 독점 커뮤니티 및 Discord 채널: 이 경우, 모든 커뮤니티 채널 또는 일부 채널에 대한 액세스가 토큰 보유자로 제한됩니다. 대표적인 예로, 새로운 웹 3.0 빌더의 교육과 영향력을 가속화하는 것을 목표로 하는 DAO인 Developer DAO가 있습니다. 개발자 DAO 디스코드에 액세스하려면 오리지널 Devs for Revolution (D4R)  NFT 또는 400개 이상의 코드 토큰 중 하나를 보유해야 합니다.
  • 온라인 경험: 온라인 경험도 토큰 게이팅이 가능합니다. 토큰 게이트 메타버스 공간에서 온라인 웨비나 및 커뮤니티 밋업에 이르기까지, 이 모델은 특정 토큰 보유자가 메타버스 또는 게임의 독점 영역에 액세스할 수 있는 SandBox 와 Decentraland와 같은 플랫폼에서 예시됩니다.
  • 토큰 게이트 콘텐츠: 기사, 동영상 콘텐츠, 교육 리소스에 대한 액세스도 토큰 게이트를 적용하여 사용자에게 독점적인 경험을 제공할 수 있습니다. 예를 들어 독점 기사에 대한 액세스 권한을 토큰으로 부여하는 The Block이 있습니다.
  • 실생활 이벤트: 토큰 게이팅이 제공하는 흥미로운 기능으로 인해 실생활에서 특별한 이벤트에 대한 액세스가 혁신적으로 변화했습니다. 간단히 말해, 토큰 게이팅은 독점 콘서트, VIP 파티, 컨퍼런스에 대한 액세스를 관리하는 데 사용할 수 있습니다. 이 모델은 Deadfellaz와 Bored Ape Yacht Club과 같은 NFT 커뮤니티가 토큰 게이트 이벤트를 주최하여 필요한 NFT를 소유해야만 참석할 수 있는 시나리오를 예로 들 수 있습니다.

토큰 게이팅은 온라인 커뮤니티와 그 밖의 영역에서 접근과 독점성을 재정의하고 있으며, 디지털 자산 분야에서 혁신적인 개념입니다. 이 튜토리얼에서는 Klaytn에서 ChainSafe SDK와 Thirdweb을 사용하여 토큰 게이팅된 Unity 게임을 빌드합니다.

최종 결과물은 아래와 같습니다. 

전제 조건

  1. Download Unity Hub을 다운로드 받으세요. 
  2. Chainsafe dashboard 로부터 Project ID를 등록하세요. 

시작하기

이 글이 끝날 때쯤이면 체인세이프 SDK와 Thirdweb을 활용하여 Unity 게임을 토큰 게이팅할 수 있습니다. 시작하려면 이 단계별 가이드를 따르세요.

ChainSafe Gaming SDK 다운로드

  • ChainSafe 깃허브 리포지토리로 이동하여 web3.unity라는 이름의 리포지토리를 검색합니다.
  • releases로 이동하여 버전 2.1.0 릴리스를 클릭합니다.
  • web3.unitypackage를 클릭하여 패키지를 다운로드합니다.

Unity 프로젝트 초기화 및 설정

  • Unity 허브와 Unity 에디터가 설치되어 있는지 확인합니다(이 가이드에서는 Unity 에디터 버전 2022.3.16f1을 설치했습니다).
  • 새 3D Unity 프로젝트를 생성합니다: 새 프로젝트를 생성하려면 다음과 같이 하세요:
    • Projects 탭으로 이동
    • New project 버튼 클릭
  • All templates 선택 (3D 템플릿 사용)
  • Project Settings 탭에서 프로젝트 이름 필드 채우기
  • Create project 클릭

ChainSafe SDK를 프로젝트에 임포트하기

  • 설치 섹션에서 다운로드한 web3.unitypackage 파일을 Unity 프로젝트에 드래그합니다.
  • ChainSafe SDK를 원활하게 사용하려면 NewtonSoft 패키지를 설치하는 것이 좋습니다. 이는 콘솔에서 아래와 같은 오류를 방지하는 데 중요합니다:
  • I다음과 같이 패키지를 가져옵니다: Window -> Package Manager -> My Assets -> JSON. NET For Unity -> Import 

ChainSafe 서버 설정

Unity 프로젝트를 설정하는 동안 SDK를 사용하여 성공적으로 빌드하려면 프로젝트를 ChainSafe에 등록해야 합니다. 위 단계에서 ChainSafe SDK를 임포트하는 동안 ChainSafeServerSettings 프롬프트가 표시되는 경우 다음을 수행합니다.

  • 프롬프트 상자의 각 필드에 필요한 값(Project ID, Chain ID, Chain, Network, RPC)을 입력합니다.
  • Save Settings 버튼을 클릭합니다. 

자세한 내용은 이 가이드를 참조하세요. 

WebLogin Prefab 및 Gaming Scene 설정하기

이 섹션에서는 Web3 지갑 연결을 활성화하기 위해 WebLogin 프리팹을 설정하고, 토큰 게이팅 작업이 이루어지는 게임 씬을 설정해 보겠습니다. 

  • Under Assets → Web3Unity → Scenes에서 WebLogin을 더블클릭합니다. 이 프리팹은 WebGL 프로젝트에서 지갑을 연결하는 데 사용되는 프리팹입니다.
  • Go to File → Build Settings → WebGL → Switch Platform 으로 이동합니다. 
  • 같은 창에서 Add Open Scenes(오른쪽 상단)를 클릭하여 프로젝트를 실행할 때 표시되는 첫 번째 장면으로 로그인 장면을 추가합니다.
  • 같은 창에서 Player Settings → Player → Resolution and Presentation을 클릭하고, under WebGL Template에서 Unity 버전과 동일한 것을 선택합니다(예시의 경우 WebGL 2020).

Gaming Scene

  • Unity 프로젝트로 돌아갑니다. 에셋에서 Scenes을 선택하고 SampleScene(현재 게임 씬)을 더블클릭하여 두 번째 씬으로 사용합니다(참고로 첫 번째 씬은 로그인 씬입니다).
  • File → Build Settings → Add Open Scenes로 이동합니다. SampleScene이 WebLogin 씬 아래 나타날 것입니다. SampleScene은 컨트랙트를 읽고 쓰는 버튼을 만드는 곳입니다. 이 장면은 WebLogin 후에 팝업됩니다.
*Note: 순서가 중요하므로 WebLogin 장면이 맨 위에 있는지 확인하세요.

Thirdweb을 사용하여 NFT 스마트 컨트랙트 생성 및 배포하기

이 섹션에서는 Thirdweb 대시보드를 사용하여 NFT 드롭 컨트랙트를 배포하겠습니다. 이 컨트랙트를 통해 플레이어는 NFT 생성자가 설정한 청구 조건을 충족한 NFT를 청구할 수 있습니다.  다음 단계에 따라 NFT 드랍 컨트랙트를 생성하고 배포할 것입니다:

  1. contracts 탭의 explore 섹션으로 이동하여 NFT Drop 컨트랙트를 선택합니다.

2. Deploy Now 버튼을 클릭합니다. 

3. 컨트랙트 파라미터인 NAME (YouniversePassCard), SYMBOL (YPC), Description 등을 입력하고, 배포할 체인(Klaytn Testnet Baobab)을 설정한 후 Deploy Now 버튼을 누릅니다.

4. 트랜잭션을 확인하고 컨트랙트가 배포되어 컨트랙트 대시보드에 추가될 때까지 기다립니다.

5. 새로 배포된 컨트랙트 대시보드가 로드되면, NFT 섹션으로 이동하여 Single Upload 버튼을 클릭하여 새 토큰을 Lazy 민트합니다.

6. 발행하려는 NFT의 메타데이터를 입력하세요.

7. 트랜잭션을 확인하고 발행 프로세스가 완료될 때까지 기다립니다. 

다음은 게임 플레이어가 쉽게 청구할 수 있도록 새로 발행된 NFT의 클레임 조건을 설정하는 것입니다. 클레임 조건을 설정하려면 다음과 같이 하세요:

  • 확장 탭 아래의 Claim Condition섹션으로 이동하여 Add Phase 버튼을 클릭합니다.
  • 단계 추가 옵션에서 Public 옵션을 선택합니다.
  • Fill in the claim conditions you want during this claim phase. As you can see below, we did set the following:이 클레임 단계에서 원하는 조건을 입력합니다. 본 예시에서는 다음과 같이 설정했습니다:
    • 이 단계에서 얼마나 많은 NFT를 드롭할 것인가: Unlimited
    • 각 NFT를 청구하는 데 클레임할 금액: 0 KLAY
    • 지갑당 청구할 수 있는 NFT 수: 1
  • Save Phases 버튼을 클릭하여 이 청구 단계를 저장합니다.

게임 오브젝트 만들기 

이 섹션에서는 3개의 게임 객체와 그에 해당하는 하위 컴포넌트를 생성하겠습니다:

a. playGame: 클릭하면 플레이어가 유니버스 패스카드 NFT를 가지고 있는지, 게임을 시작할 자격이 있는지 확인하는 버튼 컴포넌트입니다.
b. hasNFT: 텍스트와 버튼 UI 컴포넌트의 조합입니다. 플레이어가 패스카드 NFT를 가지고 있을 때 표시됩니다.
c. noNFT : 텍스트와 버튼 UI 구성 요소의 조합입니다. 플레이어가 PassCard NFT를 가지고 있지 않을 때 표시됩니다.

playGame 컴포넌트를 생성하려면,

  • Sample Scene을 마우스 오른쪽 버튼으로 클릭하고 GameObject → UI → Button (Text Mesh Pro)을 클릭한 다음 버튼의 이름을 playGame으로 변경합니다.
  • 텍스트 UI 컴포넌트의 이름을 playGame으로 변경합니다.

hasNFT 컴포넌트를 생성합니다.

  • Canvas 컴포넌트를 우클릭하고, GameObject → UI → Text Mesh Pro를 클릭한 다음 텍스트 오브젝트의 이름을 hasNFT로 변경합니다.
  • 텍스트 오브젝트에 패스카드 NFT가 있는 것으로 확인된 경우 플레이어에게 표시할 텍스트를 채워야 합니다. Ex) “Proceed to enter Youniverse. You have the PassCard”
  • hasNFT 컴포넌트를 마우스 오른쪽 버튼으로 클릭하고 GameObject → UI → Button (Text Mesh Pro)을 클릭한 다음 텍스트 이름을 startGame으로 변경합니다. 이 버튼을 통해 패스카드 NFT 소유자는 게임을 시작할 수 있습니다.
  • 버튼 텍스트의 이름을 startGame으로 반드시 변경해야 합니다.

noNFT 컴포넌트를 생성하려면,

  • Right-click on the Canvas를 우클릭하고, GameObject → UI → Text Mesh Pro를 클릭한 다음 텍스트 오브젝트의 이름을 noNFT로 변경합니다.
  • 텍스트 오브젝트에 패스카드 NFT가 없는 것으로 확인되면 플레이어에게 표시할 텍스트를 채우세요. Ex)  “You can’t proceed to enter Youniverse. You do not have the PassCard”
  • noNFT component를 우클릭하고, GameObject → UI → Button (Text Mesh Pro)을 클릭한 다음 텍스트 이름을 claimNFT로 변경합니다. 이 버튼을 사용하면 패스카드 NFT 보유자는 패스카드를 청구할 수 없습니다.
  • 버튼 텍스트의 이름을 claimNFT로 반드시 변경해야 합니다.

Unity에서 C# 스크립트 생성

  • Asset tab에서 Scenes을 우클릭하고, Create → C# Script를 클릭한 다음 이름을 tokenGating으로 변경합니다.
  • VS Code에서 스크립트를 열고 아래 코드를 붙여넣습니다.
  • 스크립트에서 컨트랙트 주소와 같은 일부 값을 필요에 맞게 변경할 수 있습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Numerics;
using UnityEngine.UI;
using Newtonsoft.Json;
using Web3Unity.Scripts.Library.ETHEREUEM.EIP;
using Web3Unity.Scripts.Library.Ethers.Providers;
using Web3Unity.Scripts.Library.Ethers.Contracts;
public class tokenGating : MonoBehaviour
{
    public GameObject playGame;
    public GameObject hasNFT;
    public GameObject noNFT;
    private string contractAddress = "PASTE CONTRACT ADDRESS";
    private readonly string contractAbi = "[ { \"type\": \"constructor\", \"name\": \"\", \"inputs\": [], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"error\", \"name\": \"ApprovalCallerNotOwnerNorApproved\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"ApprovalQueryForNonexistentToken\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"ApprovalToCurrentOwner\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"ApproveToCaller\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"BalanceQueryForZeroAddress\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"MintToZeroAddress\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"MintZeroQuantity\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"OwnerQueryForNonexistentToken\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"TransferCallerNotOwnerNorApproved\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"TransferFromIncorrectOwner\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"TransferToNonERC721ReceiverImplementer\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"TransferToZeroAddress\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"error\", \"name\": \"URIQueryForNonexistentToken\", \"inputs\": [], \"outputs\": [] }, { \"type\": \"event\", \"name\": \"Approval\", \"inputs\": [ { \"type\": \"address\", \"name\": \"owner\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"approved\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"tokenId\", \"indexed\": true, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"ApprovalForAll\", \"inputs\": [ { \"type\": \"address\", \"name\": \"owner\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"operator\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"bool\", \"name\": \"approved\", \"indexed\": false, \"internalType\": \"bool\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"BatchMetadataUpdate\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_fromTokenId\", \"indexed\": false, \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"_toTokenId\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"ClaimConditionsUpdated\", \"inputs\": [ { \"type\": \"tuple[]\", \"name\": \"claimConditions\", \"components\": [ { \"type\": \"uint256\", \"name\": \"startTimestamp\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"maxClaimableSupply\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"supplyClaimed\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"quantityLimitPerWallet\", \"internalType\": \"uint256\" }, { \"type\": \"bytes32\", \"name\": \"merkleRoot\", \"internalType\": \"bytes32\" }, { \"type\": \"uint256\", \"name\": \"pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"currency\", \"internalType\": \"address\" }, { \"type\": \"string\", \"name\": \"metadata\", \"internalType\": \"string\" } ], \"indexed\": false, \"internalType\": \"struct IClaimCondition.ClaimCondition[]\" }, { \"type\": \"bool\", \"name\": \"resetEligibility\", \"indexed\": false, \"internalType\": \"bool\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"ContractURIUpdated\", \"inputs\": [ { \"type\": \"string\", \"name\": \"prevURI\", \"indexed\": false, \"internalType\": \"string\" }, { \"type\": \"string\", \"name\": \"newURI\", \"indexed\": false, \"internalType\": \"string\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"DefaultRoyalty\", \"inputs\": [ { \"type\": \"address\", \"name\": \"newRoyaltyRecipient\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"newRoyaltyBps\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"FlatPlatformFeeUpdated\", \"inputs\": [ { \"type\": \"address\", \"name\": \"platformFeeRecipient\", \"indexed\": false, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"flatFee\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"Initialized\", \"inputs\": [ { \"type\": \"uint8\", \"name\": \"version\", \"indexed\": false, \"internalType\": \"uint8\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"MaxTotalSupplyUpdated\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"maxTotalSupply\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"MetadataFrozen\", \"inputs\": [], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"OwnerUpdated\", \"inputs\": [ { \"type\": \"address\", \"name\": \"prevOwner\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"newOwner\", \"indexed\": true, \"internalType\": \"address\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"PlatformFeeInfoUpdated\", \"inputs\": [ { \"type\": \"address\", \"name\": \"platformFeeRecipient\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"platformFeeBps\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"PlatformFeeTypeUpdated\", \"inputs\": [ { \"type\": \"uint8\", \"name\": \"feeType\", \"indexed\": false, \"internalType\": \"enum IPlatformFee.PlatformFeeType\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"PrimarySaleRecipientUpdated\", \"inputs\": [ { \"type\": \"address\", \"name\": \"recipient\", \"indexed\": true, \"internalType\": \"address\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"RoleAdminChanged\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"indexed\": true, \"internalType\": \"bytes32\" }, { \"type\": \"bytes32\", \"name\": \"previousAdminRole\", \"indexed\": true, \"internalType\": \"bytes32\" }, { \"type\": \"bytes32\", \"name\": \"newAdminRole\", \"indexed\": true, \"internalType\": \"bytes32\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"RoleGranted\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"indexed\": true, \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"sender\", \"indexed\": true, \"internalType\": \"address\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"RoleRevoked\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"indexed\": true, \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"sender\", \"indexed\": true, \"internalType\": \"address\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"RoyaltyForToken\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"tokenId\", \"indexed\": true, \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"royaltyRecipient\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"royaltyBps\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"TokenURIRevealed\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"index\", \"indexed\": true, \"internalType\": \"uint256\" }, { \"type\": \"string\", \"name\": \"revealedURI\", \"indexed\": false, \"internalType\": \"string\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"TokensClaimed\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"claimConditionIndex\", \"indexed\": true, \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"claimer\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"receiver\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"startTokenId\", \"indexed\": false, \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"quantityClaimed\", \"indexed\": false, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"TokensLazyMinted\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"startTokenId\", \"indexed\": true, \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"endTokenId\", \"indexed\": false, \"internalType\": \"uint256\" }, { \"type\": \"string\", \"name\": \"baseURI\", \"indexed\": false, \"internalType\": \"string\" }, { \"type\": \"bytes\", \"name\": \"encryptedBaseURI\", \"indexed\": false, \"internalType\": \"bytes\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"event\", \"name\": \"Transfer\", \"inputs\": [ { \"type\": \"address\", \"name\": \"from\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"to\", \"indexed\": true, \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"tokenId\", \"indexed\": true, \"internalType\": \"uint256\" } ], \"outputs\": [], \"anonymous\": false }, { \"type\": \"function\", \"name\": \"DEFAULT_ADMIN_ROLE\", \"inputs\": [], \"outputs\": [ { \"type\": \"bytes32\", \"name\": \"\", \"internalType\": \"bytes32\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"approve\", \"inputs\": [ { \"type\": \"address\", \"name\": \"to\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"balanceOf\", \"inputs\": [ { \"type\": \"address\", \"name\": \"owner\", \"internalType\": \"address\" } ], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"batchFrozen\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"burn\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"claim\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_receiver\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_quantity\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"_currency\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"tuple\", \"name\": \"_allowlistProof\", \"components\": [ { \"type\": \"bytes32[]\", \"name\": \"proof\", \"internalType\": \"bytes32[]\" }, { \"type\": \"uint256\", \"name\": \"quantityLimitPerWallet\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"currency\", \"internalType\": \"address\" } ], \"internalType\": \"struct IDrop.AllowlistProof\" }, { \"type\": \"bytes\", \"name\": \"_data\", \"internalType\": \"bytes\" } ], \"outputs\": [], \"stateMutability\": \"payable\" }, { \"type\": \"function\", \"name\": \"claimCondition\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"currentStartId\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"count\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"contractType\", \"inputs\": [], \"outputs\": [ { \"type\": \"bytes32\", \"name\": \"\", \"internalType\": \"bytes32\" } ], \"stateMutability\": \"pure\" }, { \"type\": \"function\", \"name\": \"contractURI\", \"inputs\": [], \"outputs\": [ { \"type\": \"string\", \"name\": \"\", \"internalType\": \"string\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"contractVersion\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint8\", \"name\": \"\", \"internalType\": \"uint8\" } ], \"stateMutability\": \"pure\" }, { \"type\": \"function\", \"name\": \"encryptDecrypt\", \"inputs\": [ { \"type\": \"bytes\", \"name\": \"data\", \"internalType\": \"bytes\" }, { \"type\": \"bytes\", \"name\": \"key\", \"internalType\": \"bytes\" } ], \"outputs\": [ { \"type\": \"bytes\", \"name\": \"result\", \"internalType\": \"bytes\" } ], \"stateMutability\": \"pure\" }, { \"type\": \"function\", \"name\": \"encryptedData\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"bytes\", \"name\": \"\", \"internalType\": \"bytes\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"freezeBatchBaseURI\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_index\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"getActiveClaimConditionId\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getApproved\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getBaseURICount\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getBatchIdAtIndex\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_index\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getClaimConditionById\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_conditionId\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"tuple\", \"name\": \"condition\", \"components\": [ { \"type\": \"uint256\", \"name\": \"startTimestamp\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"maxClaimableSupply\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"supplyClaimed\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"quantityLimitPerWallet\", \"internalType\": \"uint256\" }, { \"type\": \"bytes32\", \"name\": \"merkleRoot\", \"internalType\": \"bytes32\" }, { \"type\": \"uint256\", \"name\": \"pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"currency\", \"internalType\": \"address\" }, { \"type\": \"string\", \"name\": \"metadata\", \"internalType\": \"string\" } ], \"internalType\": \"struct IClaimCondition.ClaimCondition\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getDefaultRoyaltyInfo\", \"inputs\": [], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" }, { \"type\": \"uint16\", \"name\": \"\", \"internalType\": \"uint16\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getFlatPlatformFeeInfo\", \"inputs\": [], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getPlatformFeeInfo\", \"inputs\": [], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" }, { \"type\": \"uint16\", \"name\": \"\", \"internalType\": \"uint16\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getPlatformFeeType\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint8\", \"name\": \"\", \"internalType\": \"enum IPlatformFee.PlatformFeeType\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getRevealURI\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_batchId\", \"internalType\": \"uint256\" }, { \"type\": \"bytes\", \"name\": \"_key\", \"internalType\": \"bytes\" } ], \"outputs\": [ { \"type\": \"string\", \"name\": \"revealedURI\", \"internalType\": \"string\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getRoleAdmin\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" } ], \"outputs\": [ { \"type\": \"bytes32\", \"name\": \"\", \"internalType\": \"bytes32\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getRoleMember\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" }, { \"type\": \"uint256\", \"name\": \"index\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"address\", \"name\": \"member\", \"internalType\": \"address\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getRoleMemberCount\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" } ], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"count\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getRoyaltyInfoForToken\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" }, { \"type\": \"uint16\", \"name\": \"\", \"internalType\": \"uint16\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"getSupplyClaimedByWallet\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_conditionId\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"_claimer\", \"internalType\": \"address\" } ], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"supplyClaimedByWallet\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"grantRole\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"internalType\": \"address\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"hasRole\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"internalType\": \"address\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"hasRoleWithSwitch\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"internalType\": \"address\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"initialize\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_defaultAdmin\", \"internalType\": \"address\" }, { \"type\": \"string\", \"name\": \"_name\", \"internalType\": \"string\" }, { \"type\": \"string\", \"name\": \"_symbol\", \"internalType\": \"string\" }, { \"type\": \"string\", \"name\": \"_contractURI\", \"internalType\": \"string\" }, { \"type\": \"address[]\", \"name\": \"_trustedForwarders\", \"internalType\": \"address[]\" }, { \"type\": \"address\", \"name\": \"_saleRecipient\", \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"_royaltyRecipient\", \"internalType\": \"address\" }, { \"type\": \"uint128\", \"name\": \"_royaltyBps\", \"internalType\": \"uint128\" }, { \"type\": \"uint128\", \"name\": \"_platformFeeBps\", \"internalType\": \"uint128\" }, { \"type\": \"address\", \"name\": \"_platformFeeRecipient\", \"internalType\": \"address\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"isApprovedForAll\", \"inputs\": [ { \"type\": \"address\", \"name\": \"owner\", \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"operator\", \"internalType\": \"address\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"isEncryptedBatch\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_batchId\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"isTrustedForwarder\", \"inputs\": [ { \"type\": \"address\", \"name\": \"forwarder\", \"internalType\": \"address\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"lazyMint\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_amount\", \"internalType\": \"uint256\" }, { \"type\": \"string\", \"name\": \"_baseURIForTokens\", \"internalType\": \"string\" }, { \"type\": \"bytes\", \"name\": \"_data\", \"internalType\": \"bytes\" } ], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"batchId\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"maxTotalSupply\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"multicall\", \"inputs\": [ { \"type\": \"bytes[]\", \"name\": \"data\", \"internalType\": \"bytes[]\" } ], \"outputs\": [ { \"type\": \"bytes[]\", \"name\": \"results\", \"internalType\": \"bytes[]\" } ], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"name\", \"inputs\": [], \"outputs\": [ { \"type\": \"string\", \"name\": \"\", \"internalType\": \"string\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"nextTokenIdToClaim\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"nextTokenIdToMint\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"owner\", \"inputs\": [], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"ownerOf\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"primarySaleRecipient\", \"inputs\": [], \"outputs\": [ { \"type\": \"address\", \"name\": \"\", \"internalType\": \"address\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"renounceRole\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"internalType\": \"address\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"reveal\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_index\", \"internalType\": \"uint256\" }, { \"type\": \"bytes\", \"name\": \"_key\", \"internalType\": \"bytes\" } ], \"outputs\": [ { \"type\": \"string\", \"name\": \"revealedURI\", \"internalType\": \"string\" } ], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"revokeRole\", \"inputs\": [ { \"type\": \"bytes32\", \"name\": \"role\", \"internalType\": \"bytes32\" }, { \"type\": \"address\", \"name\": \"account\", \"internalType\": \"address\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"royaltyInfo\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"salePrice\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"address\", \"name\": \"receiver\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"royaltyAmount\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"safeTransferFrom\", \"inputs\": [ { \"type\": \"address\", \"name\": \"from\", \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"to\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"safeTransferFrom\", \"inputs\": [ { \"type\": \"address\", \"name\": \"from\", \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"to\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" }, { \"type\": \"bytes\", \"name\": \"_data\", \"internalType\": \"bytes\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setApprovalForAll\", \"inputs\": [ { \"type\": \"address\", \"name\": \"operator\", \"internalType\": \"address\" }, { \"type\": \"bool\", \"name\": \"approved\", \"internalType\": \"bool\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setClaimConditions\", \"inputs\": [ { \"type\": \"tuple[]\", \"name\": \"_conditions\", \"components\": [ { \"type\": \"uint256\", \"name\": \"startTimestamp\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"maxClaimableSupply\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"supplyClaimed\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"quantityLimitPerWallet\", \"internalType\": \"uint256\" }, { \"type\": \"bytes32\", \"name\": \"merkleRoot\", \"internalType\": \"bytes32\" }, { \"type\": \"uint256\", \"name\": \"pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"currency\", \"internalType\": \"address\" }, { \"type\": \"string\", \"name\": \"metadata\", \"internalType\": \"string\" } ], \"internalType\": \"struct IClaimCondition.ClaimCondition[]\" }, { \"type\": \"bool\", \"name\": \"_resetClaimEligibility\", \"internalType\": \"bool\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setContractURI\", \"inputs\": [ { \"type\": \"string\", \"name\": \"_uri\", \"internalType\": \"string\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setDefaultRoyaltyInfo\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_royaltyRecipient\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_royaltyBps\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setFlatPlatformFeeInfo\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_platformFeeRecipient\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_flatFee\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setMaxTotalSupply\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_maxTotalSupply\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setOwner\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_newOwner\", \"internalType\": \"address\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setPlatformFeeInfo\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_platformFeeRecipient\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_platformFeeBps\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setPlatformFeeType\", \"inputs\": [ { \"type\": \"uint8\", \"name\": \"_feeType\", \"internalType\": \"enum IPlatformFee.PlatformFeeType\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setPrimarySaleRecipient\", \"inputs\": [ { \"type\": \"address\", \"name\": \"_saleRecipient\", \"internalType\": \"address\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"setRoyaltyInfoForToken\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_tokenId\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"_recipient\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_bps\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"supportsInterface\", \"inputs\": [ { \"type\": \"bytes4\", \"name\": \"interfaceId\", \"internalType\": \"bytes4\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"symbol\", \"inputs\": [], \"outputs\": [ { \"type\": \"string\", \"name\": \"\", \"internalType\": \"string\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"tokenURI\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [ { \"type\": \"string\", \"name\": \"\", \"internalType\": \"string\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"totalMinted\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"totalSupply\", \"inputs\": [], \"outputs\": [ { \"type\": \"uint256\", \"name\": \"\", \"internalType\": \"uint256\" } ], \"stateMutability\": \"view\" }, { \"type\": \"function\", \"name\": \"transferFrom\", \"inputs\": [ { \"type\": \"address\", \"name\": \"from\", \"internalType\": \"address\" }, { \"type\": \"address\", \"name\": \"to\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"tokenId\", \"internalType\": \"uint256\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"updateBatchBaseURI\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_index\", \"internalType\": \"uint256\" }, { \"type\": \"string\", \"name\": \"_uri\", \"internalType\": \"string\" } ], \"outputs\": [], \"stateMutability\": \"nonpayable\" }, { \"type\": \"function\", \"name\": \"verifyClaim\", \"inputs\": [ { \"type\": \"uint256\", \"name\": \"_conditionId\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"_claimer\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_quantity\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"_currency\", \"internalType\": \"address\" }, { \"type\": \"uint256\", \"name\": \"_pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"tuple\", \"name\": \"_allowlistProof\", \"components\": [ { \"type\": \"bytes32[]\", \"name\": \"proof\", \"internalType\": \"bytes32[]\" }, { \"type\": \"uint256\", \"name\": \"quantityLimitPerWallet\", \"internalType\": \"uint256\" }, { \"type\": \"uint256\", \"name\": \"pricePerToken\", \"internalType\": \"uint256\" }, { \"type\": \"address\", \"name\": \"currency\", \"internalType\": \"address\" } ], \"internalType\": \"struct IDrop.AllowlistProof\" } ], \"outputs\": [ { \"type\": \"bool\", \"name\": \"isOverride\", \"internalType\": \"bool\" } ], \"stateMutability\": \"view\" } ]";

    
    public string playerAccount;

     void Start() 
     { 
        string account = PlayerPrefs.GetString("Account");
        playerAccount = account;
        print("User account is :" + " " + playerAccount);     
        hasNFT = GameObject.Find("Canvas/hasNFT");
        noNFT = GameObject.Find("Canvas/noNFT");
        playGame = GameObject.Find("Canvas/playGame");
        if(hasNFT) {
            hasNFT.SetActive(false);
            print("hasNFT is false at start");     
        }
        if(noNFT) {
            noNFT.SetActive(false);
            print("noNFT is false at start");     
        }  
     }
    
    // calls the "balanceOf" function
    async public void checkBalance()
    {
        var provider = new JsonRpcProvider("https://klaytn-baobab-rpc.allthatnode.com:8551");
        var contract = new Contract(contractAbi, contractAddress, provider);
        var calldata = await contract.Call("balanceOf", new object[]
        {
            PlayerPrefs.GetString("Account"),
        });
        Debug.Log(calldata[0].ToString());
        string balance = calldata[0].ToString();
        float bal = float.Parse(balance);
        if(bal > 0){
            hasNFT.SetActive(true);
        } else {
            noNFT.SetActive(true); 
        }      
    }
    // call claim function
        async public void claimNFT()
    {
        int quantity = 1;
        string currencyAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
        int pricePerToken = 0;
        object[] allowProof = new object[]{
            new string[] {},
            quantity,
            pricePerToken,
            currencyAddress
        };
        string data = "0x";
      
        // function name
        string method = "claim";
        // put arguments in an array of string
        object[] obj = new object[]{playerAccount, quantity, currencyAddress, pricePerToken, allowProof, data};
        // serialize arguments
        string args = JsonConvert.SerializeObject(obj);
        // value in peb (wei) in a transaction
        string value = "0";
        // gas limit: REQUIRED
        string gasLimit = "1000000";
        // gas price: REQUIRED
        string gasPrice = "250000000000";
        try
        {          
        string response = await Web3GL.SendContract(method, contractAbi, contractAddress, args, value, gasLimit, gasPrice);            
            Debug.Log(response);
        } catch(Exception e) 
        {
            Debug.LogException(e, this);
        }
    }
}

C# 스크립트와 게임 오브젝트 연결하기

이 섹션에서는 다음 버튼을 토큰게이팅 스크립트의 각 함수와 연결해 보겠습니다.

A. playGame: 

  • Hierarchy 창에서 playGame 버튼을 클릭합니다. 
  • 스크립트를 오른쪽 창(Add Component 탭)으로 드래그합니다. 
  • ➕ 버튼을 클릭하여 OnClick() 함수를 추가합니다.
  • Hierarchy창에서 playGame 버튼을 On Click() 함수로 드래그합니다.
  • No Function → tokenGating → checkBalance() 를 클릭합니다. 

B. claimNFT

  • Hierarchy 창에서  noNFT 컴포넌트의 claimNFT 버튼으로 이동합니다.
  • 스크립트를 오른쪽 창(Add Component 탭)으로 드래그합니다.
  • ➕ 버튼을 클릭하여 OnClick() 함수를 추가합니다.
  • Hierarchy창에서 claimNFT 버튼을 On Click() 함수로 드래그합니다.
  • No Function → tokenGating → claimNFT() 을 클릭합니다. 

게임 애플리케이션 테스트 및 실행

이 섹션에서는 게임 애플리케이션에서 토큰 생성 기능을 테스트해 보겠습니다.  Youniverse PassCard NFT를 소유한 사람은 누구나 게임을 시작하라는 메시지가 표시되고, 그렇지 않은 경우 Youniverse PassCard NFT 1개를 받을 수 있을 것으로 예상합니다.

이를 실제로 확인하기 위해선, 

  • 프로젝트를 빌드하고 실행합니다: Navigate to File → Build and Run
  • 프로젝트가 빌드되고 실행되면, 브라우저에 webLogin scene인 탭이 열립니다.
  • Metamask를 연결하기위해 Login을 클릭합니다. 
  • 연결되면 playGame을 클릭하여 플레이어가 자격이 있는지 확인합니다. 

플레이어가 자격이 없는 경우, 화면에 이 메시지가 표시되고 Youniverse PassCard NFT를 신청할 수 있는 버튼이 표시됩니다. 자격이 없는 플레이어가 NFT를 신청하면 게임을 시작할 수 있습니다.

플레이어가 자격을 갖추면 화면에 아래 메시지와 게임 시작 버튼이 표시됩니다.

결론

이 튜토리얼에서는 토큰 게이팅의 개념과 실제 적용 방법, 그리고 Unity 게임 애플리케이션을 토큰 게이팅하는 방법을 배웠습니다. 이 듀토리얼을 잘 따라했다면, Unity 게임 애플리케이션을 성공적으로 토큰 게이팅 했을 것입니다. 

여기서부터, 게임 시작 버튼에 기능을 추가하는 등 게임에서 다른 옵션도 살펴볼 수 있습니다. 더 자세한 정보가 필요하시면 Klaytn DocsChainsafe Docs를 참조하세요. 질문이 있으시다면 Klaytn Forum에 남겨주세요.