NFT 스마트 계약 생성 및 배포

이 섹션에서는 NFT 마켓플레이스를 설정하는 데 필요한 NFT 스마트 계약을 생성 및 배포하는 방법에 대해 알아봅니다.

블록체인 앱 빌더를 사용하여 NFT를 관리, 소유 및 이전하는 스마트 계약을 생성할 수 있습니다. 이 솔루션의 핵심 요소는 Oracle Blockchain Platform을 사용하여 NFT를 민트 및 전송하는 기능입니다.

먼저 NFT 사양 파일을 생성한 다음 스마트 계약을 Oracle Blockchain Platform 인스턴스에 배포합니다. 그런 다음 스마트 계약을 테스트할 수 있습니다.

블록체인 앱 빌더 설치

Oracle Blockchain Platform 인스턴스를 프로비저닝한 후 다음 단계를 완료합니다.

  1. Oracle Blockchain Platform 콘솔에서 개발자 툴 탭을 열고 블록체인 앱 작성기 창을 선택합니다.
  2. 다운로드 섹션에서 CLI(명령행 인터페이스) 툴 아카이브 또는 VS(Visual Studio) 코드 확장을 다운로드한 다음 로컬에서 설정합니다.

샘플 NFT 사양 파일 사용자 정의

샘플 NFT 사양 파일은 Blockchain App Builder에 포함되어 있습니다. 다음 예에 표시된 대로 이를 사용하여 필요에 맞게 조정할 수 있습니다.


 assets:
    - name: ArtCollection
      type: token
      symbol: ART      #mandatory
      standard: erc721+   
      anatomy:
        type: nonfungible
        unit: whole
      behavior:
        - indivisible      # mandatory
        - singleton        # mandatory
        - mintable:        # mandatory
            max_min_quantity: 20000
        - transferable
        - burnable
        - roles:
            minter_role_name: minter
      properties:
        - name: price
          type: number
        - name: on_sale_flag
          type: boolean
      metadata:
        - name: painting_name
          type: string
        - name: description
          type: string
        - name: image
          type: string
        - name: painter_name
          type: string
 customMethods:
    - executeQuery
    - "createAccountByConsumers(org_id: string, user_id: string, token_type: string)" # Create accounts for consumers while signing up
    - "sell(token_id: string, selling_price: number)" # Post the token for selling in the marketplace
    - "buyWithTokens(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, fungible_token_id: string, amount_paid: number)"  # Buy the NFT after paying the using FT Tokens 
    - "buyWithDirectPayment(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, amount_paid: number)"  # Buy the NFT after paying the amount using payment gateway

사용자 정의 등록 정보 및 메소드를 추가하여 NFT 체인코드에 대해 이 사양을 확장할 수 있습니다. 블록체인 앱 빌더가 생성한 NFT 체인코드는 ERC-721 표준을 기반으로 합니다.

Visual Studio Code 확장을 사용하여 체인코드(스마트 계약)를 생성하려면 다음 단계를 완료하십시오.

  1. 체인코드 섹션에서 + 아이콘을 누릅니다. Chaincode Details 창이 열립니다.
  2. 체인코드 프로젝트 생성에 필요한 필드를 완성합니다.
    • 체인코드 프로젝트에 대한 이름을 입력합니다.
    • 체인코드 메소드를 생성할 언어로 TypeScript 또는 Go을 선택합니다.
    • 이전에 생성한 입력 사양 파일을 선택합니다.
    • 프로젝트를 생성할 위치를 입력하십시오.
  3. 생성을 누릅니다.

다음 UI 스크린샷은 체인 코드 생성 창을 보여줍니다.

다음은 blockchain_chaincode_details.png에 대한 설명입니다.
blockchain_chaincode_details.png 그림에 대한 설명
체인코드를 생성한 후 프로젝트 계층의 src 디렉토리 아래에 있는 파일을 검사하여 모델 및 컨트롤러 구현을 검토할 수 있습니다.
  • 모델 파일에는 토큰, 계정 등을 나타내는 생성된 모든 데이터 구조가 포함됩니다.
  • 제어기 파일에는 모든 생성된 NFT 수명 주기 방법 및 사양을 기반으로 하는 필수 검증 논리가 포함된 지원 기능이 포함됩니다.
제어기 파일에는 생성된 SDK 기능을 기반으로 업무 논리를 추가할 수 있는 사용자 정의 방법에 대한 함수 템플리트도 있습니다.

/**
*      
* BDB sql rich queries can be executed in OBP CS/EE.
* This method can be invoked only when connected to remote OBP CS/EE network.
*    
*/
@Validator(yup.string())
    public async executeQuery(query: string) {
        const result = await this.query(query);
        return result;
    }
@Validator(yup.string(), yup.string(), yup.string())
          public async createAccountByConsumers(org_id: string, user_id: string, token_type: string) {       
          //await this.Ctx.ERC721Auth.checkAuthorization('ERC721ACCOUNT.createAccount', 'TOKEN');
          return await this.Ctx.ERC721Account.createAccount(org_id, user_id, token_type);   
    }
@Validator(yup.string(), yup.number())
public async sell (token_id: string, selling_price: number) {       
          try {  
            const token = await this.Ctx.ERC721Token.get(token_id);
            const t = new ArtCollection(token)
            t.price =  selling_price;
            t.on_sale_flag = true;
            //console.log(token);           
            await this.Ctx.ERC721Token.update(t);
            return `Token ID : '${token_id}' has been posted for selling in the marketplace'`;           
          } catch(error) {
                throw new Error(error.message);
        }
}
@Validator(yup.string(), yup.string(), yup.string(), yup.string(), yup.string(), yup.string(), yup.number())
public async buyWithTokens(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, fungible_token_id: string, amount_paid: number) {
        try {  
            const token = await this.Ctx.ERC721Token.get(nonfungible_token_id);
            const t = new ArtCollection(token);
            const oChainUtil = new OChainUtils(this.Ctx.Stub);
            var msg = `Token ID : '${nonfungible_token_id}' had not been transferred'`;
            if (t.on_sale_flag==true) {
                if(t.price == amount_paid) {                   
                     // @ts-ignore
                    await oChainUtil.invokeChaincode("LoyaltyToken7", "transferTokens", [fungible_token_id, from_org_id, from_user_id, amount_paid], "marketplace");
                    const from_account_id = await this.Ctx.ERC721Account.generateAccountId(from_org_id, from_user_id);                   
                    const to_account_id = await this.Ctx.ERC721Account.generateAccountId(to_org_id, to_user_id);         
                    await this.Ctx.ERC721Token.transferFrom(from_account_id, to_account_id, t);
     
                    msg = `Token ID : '${nonfungible_token_id}' has been successfully transferred to UserID : '${to_user_id}'`;           
              }           
            }
            else {
                msg = `Token ID : '${nonfungible_token_id}' has not been transferred to UserID : '${to_user_id}' as the amount was not fully paid'`;
            }
            return msg;
       } catch(error)
          {
            throw new Error(error.message);
         }
}
@Validator(yup.string(), yup.string(), yup.string(), yup.string(), yup.string(), yup.number())
    public async buyWithDirectPayment(from_org_id: string, from_user_id: string, to_org_id: string, to_user_id: string, nonfungible_token_id: string, amount_paid: number) {
         try {  
             const token = await this.Ctx.ERC721Token.get(nonfungible_token_id);
             const t = new ArtCollection(token);
             var msg = `Token ID : '${nonfungible_token_id}' had not been transferred'`;           
           if (t.on_sale_flag==true) {
                 if(t.price == amount_paid) {                   
                 const from_account_id = await this.Ctx.ERC721Account.generateAccountId(from_org_id, from_user_id);         
                 const to_account_id = await this.Ctx.ERC721Account.generateAccountId(to_org_id, to_user_id);                   
                 await this.Ctx.ERC721Token.transferFrom(from_account_id, to_account_id, t);
                   
                 msg = `Token ID : '${nonfungible_token_id}' has been successfully transferred to UserID : '${to_user_id}'`;
                 }
             }
             else {
                 msg = `Token ID : '${nonfungible_token_id}' has not been transferred to UserID : '${to_user_id}' as the amount was not fully paid'`;
             }
             return msg;
         } catch(error) {         
             throw new Error(error.message);
         }
      }
}

스마트 계약 배포

체인코드 프로젝트를 생성한 후에 로컬로 배포할 수 있습니다.

Chaincode Details 창에서 Deploy를 선택하여 배치 마법사를 엽니다. 블록체인 앱 빌더는 테스트용으로 사용할 수 있는 Docker 컨테이너에서 실행되는 로컬 블록체인 네트워크를 포함합니다.

대상 환경에 대한 목록에서 접속 프로파일을 선택하여 Oracle Blockchain Platform 인스턴스에 체인 코드를 배치할 수도 있습니다. 생성된 NFT 체인코드에는 초기화를 위해 orgIduserId 매개변수가 필요하므로 Init Parameters도 완료하고 저장해야 합니다. orgIduserId 매개변수는 토큰 관리자 권한이 있는 사용자를 지정하는 데 사용됩니다.

스마트 계약 테스트

체인코드를 배포할 때 Oracle Blockchain Platform은 토큰 초기화, 계정 및 역할 관리, NFT 수명 주기 방법(생성, 이전, 레코딩)에 대한 REST API를 자동으로 노출합니다.

Blockchain App Builder의 Chaincode Details 창에서 Execute를 선택하거나 Postman과 같은 REST API 클라이언트를 사용하여 테스트할 수 있습니다.

다음 UI 스크린샷은 체인 코드 생성 창의 실행 탭을 보여줍니다.

다음은 blockchain_chaincode_execute.png에 대한 설명입니다.
blockchain_chaincode_execute.png 그림에 대한 설명

REST API를 사용하여 Oracle Blockchain Platform 스마트 계약 메소드를 호출하려면 POST 메소드를 사용하여 두 부분으로 구성된 URL을 지정합니다. 첫번째 부분은 Oracle Blockchain Platform의 REST 프록시 엔드포인트이며, Oracle Blockchain Platform 콘솔의 노드 탭에서 확인할 수 있습니다.

두번째 부분은 Transaction API를 사용하여 트랜잭션을 호출하는 특정 URI입니다. POST 요청을 보내려면 문서를 참조하십시오. 두 부분 모두 여기에 있는 것과 유사한 완전한 URL을 형성합니다.

https://oabcs1-iad.blockchain.ocp.oraclecloud.com:7443/restproxy/api/v2/channels/{channelName}/transactions

{channelName}을 체인코드를 배포할 때 지정한 채널 이름(예: marketplace)으로 바꿉니다. API 요청을 생성할 때 다음 단계를 완료합니다.

  • REST_Client 롤에 매핑된 userid and password에서 Basic Auth를 사용하도록 권한 부여를 설정합니다. OAuth2 토큰을 사용할 수도 있습니다. 자세한 내용은 REST API 설명서의 Use OAuth 2.0 Access Token Based Authentication을 참조하십시오.
  • Content-Type 헤더를 application/json로 설정합니다.
  • 요청 본문에 필요한 인수와 함께 체인코드 이름 및 create<TokenName>Token 메소드를 포함하여 트랜잭션 호출에 필요한 매개변수를 포함합니다.

yaml 템플리트에서 생성된 체인 코드를 기반으로 다음 텍스트는 샘플 요청 본문과 연관된 응답을 보여줍니다.

요청

{
    "chaincode": "{{NFTChaincode}}",
    "args": [
        "createArtCollectionToken",
        "{\"token_id\":\"{{NFTTokenID}}\",\"token_uri\":\"https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json\",\"metadata\":{\"painting_name\":\"Oracle - Red Bull Partnership\",\"image\":\"https://ipfs.io/ipfs/QmVap6Gkh3Cp9DiLLWvkvJHpuXpFmYB2GzU1caM57gNcAa?filename=Oracle_RedBull_NFT1.jpeg\",\"painter\":\"Alex\"},\"price\":200,\"on_sale_flag\":false}"
    ],
    "timeout": 0,
    "sync": true
}

응답

{
    "returnCode": "Success",
    "error": "",
    "result": {
        "txid": "c999922f04c3011bf25ca43624e4bb23e8900634f8e23a1648170a90274a9733",
        "payload": {
            "metadata": {
                "painter": "Alex",
                "painting_name": "Oracle - Red Bull Partnership",
                "image": "https://ipfs.io/ipfs/QmVap6Gkh3Cp9DiLLWvkvJHpuXpFmYB2GzU1caM57gNcAa?filename=Oracle_RedBull_NFT1.jpeg"
            },
            "assetType": "otoken",
            "created_by": "oaccount~eadf1b0ae857164f8681d1742b6328089a7d33ebec76d8248cb909da7a84f42a",
            "creation_date": "2022-04-28T12:08:38.000Z",
            "owner": "oaccount~eadf1b0ae857164f8681d1742b6328089a7d33ebec76d8248cb909da7a84f42a",
            "uri": "https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json",
            "is_burned": false,
            "token_id": "NFT17",
            "token_name": "artcollection",
            "symbol": "ART",
            "token_standard": "erc721+",
            "token_type": "nonfungible",
            "token_unit": "whole",
            "behaviors": [
                "indivisible",
                "singleton",
                "mintable",
                "transferable",
                "burnable",
                "roles"
            ],
            "roles": {
                "minter_role_name": "minter"
            },
            "mintable": {
                "max_mint_quantity": 20000
            },
            "token_uri": "https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json",
            "price": 200,
            "on_sale_flag": false
        },

Golang 또는 TypeScript 메소드 및 Platform REST API를 포함하여 NFT 체인코드 관리에 사용할 수 있는 방법에 대한 자세한 내용은 추가 탐색 섹션을 참조하십시오.

Oracle Content Management 웹훅 및 VBCS(Visual Builder Cloud Service)에 구축된 웹 애플리케이션에서 직접 API를 호출하거나 외부에서 호스트된 마켓플레이스 웹 애플리케이션에서 사용할 수 있도록 API 게이트웨이를 다시 매핑하고 래핑할 수 있습니다. NFT를 생성(인쇄)할 때 제공해야 하는 매개변수 중 하나는 token_uri입니다. 이 매개변수는 디지털 객체를 나타내는 JSON 파일을 가리키는 IPFS URI일 수 있습니다.

https://ipfs.io/ipfs/QmV68aiT7xw2WX8pmDbeTWpGP2or35NUFan9RagymsLpgV?filename=ArtCollection_NFT1.json

다음 단계에서는 IPFS Desktop을 사용하여 토큰 URI를 생성하는 예를 설명합니다.

  1. IPFS에 이미지 파일을 업로드하고 이미지의 URI를 저장합니다.
  2. 관련 메타데이터 필드와 함께 이미지 URI가 포함된 JSON 파일을 생성하고 업로드합니다.
  3. 링크를 공유하고 ... 아래의 JSON 파일에 복사합니다. 자세한 내용은를 참조하십시오.

{ 
    "painting_name": "Oracle - Red Bull Partnership",
    "description": "Cloud Partnership",
    "image": "https://ipfs.io/ipfs/QmVap6Gkh3Cp9DiLLWvkvJHpuXpFmYB2GzU1caM57gNcAa?filename=Oracle_RedBull.jpeg",
    "painter_name": "Alex"
}

token_uri 매개변수에 대해 JSON 파일을 가리키는 URI를 사용합니다.

create<Token-Name>Token 메소드로 전달되는 속성에 관련 메타데이터를 포함할 수도 있습니다. 그러면 간편한 검색을 위해 Oracle Blockchain Platform 원장에 메타데이터가 유지 관리됩니다.

OCM(Oracle Content Management)을 사용하여 생성된 NFT의 경우 OCM Webhook을 사용하여 API 게이트웨이를 사용하여 Oracle Functions를 호출하고 아키텍처 다이어그램에 표시된 대로 REST API 호출을 Oracle Functions에 배치합니다. 마켓플레이스 웹 애플리케이션에서 바로 블록체인 REST API를 호출하거나 API 게이트웨이 매핑을 사용하여 사용자 생성 컨텐츠를 지원할 수도 있습니다. 마켓플레이스 거래 기능은 사용자 정의 메소드를 호출하여 지불을 처리한 다음 buyWithTokensbuyWithDirectPayment 샘플 메소드와 같은 NFT 소유권 이전을 트리거할 수 있습니다.