NFTスマート・コントラクトの作成とデプロイ

この項では、NFT Marketplaceの設定に必要なNFTスマート・コントラクトを作成してデプロイする方法を学習します。

ブロックチェーン・アプリケーション・ビルダーを使用して、NFTの最小化、所有および転送を管理するスマート・コントラクトを生成します。このソリューションのコア・イネーブラは、Oracle Blockchain Platformを使用してNFTをミントして転送する機能です。

最初にNFT仕様ファイルを作成し、スマート・コントラクトをOracle Blockchain Platformインスタンスにデプロイします。その後、スマート契約をテストできます。

ブロックチェーン・アプリケーション・ビルダーのインストール

Oracle Blockchain Platformインスタンスをプロビジョニングしたら、次のステップを実行します:

  1. Oracle Blockchain Platformコンソールで、「開発者ツール」タブを開き、「ブロックチェーン・アプリケーション・ビルダー」ペインを選択します。
  2. 「ダウンロード」セクションで、コマンドライン・インタフェース(CLI)ツール・アーカイブまたはVisual Studio (VS)コード拡張をダウンロードし、ローカルに設定します。

サンプルNFT仕様ファイルのカスタマイズ

サンプルNFT仕様ファイルは、ブロックチェーン・アプリケーション・ビルダーに含まれています。次の例に示すように、これを使用してニーズに合わせて調整できます。


 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. 「チェーンコード」セクションで、「+」アイコンをクリックします。「チェーンコード詳細」ペインが開きます。
  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);
         }
      }
}

Smart Contractのデプロイ

チェーンコード・プロジェクトを作成した後は、ローカルにデプロイできます。

「チェーンコードの詳細」ペインで、「デプロイ」を選択してデプロイメント・ウィザードを開きます。ブロックチェーン・アプリケーション・ビルダーには、Dockerコンテナで実行されるローカル・ブロックチェーン・ネットワークがあり、テスト目的で使用できます。

ターゲット環境のリストから接続プロファイルを選択して、チェーンコードをOracle Blockchain Platformインスタンスにデプロイすることもできます。生成されたNFTチェーンコードには初期化のためにorgIdおよびuserIdパラメータが必要であるため、初期化パラメータを完了して保存する必要もあります。orgIdおよびuserIdパラメータを使用して、トークン管理者権限を持つユーザーを指定します。

Smart Contractのテスト

チェーンコードがデプロイされると、Oracle Blockchain Platformによって、トークン初期化、アカウントおよびロール管理、およびNFTライフサイクル・メソッド(作成、転送、書込み)用のREST APIが自動的に公開されます。

ブロックチェーン・アプリケーション・ビルダーの「チェーンコード詳細」ペインで「実行」を選択するか、PostmanなどのREST APIクライアントを使用してテストできます。

UIの次のスクリーンショットは、「チェーンコードの作成」ウィンドウの「実行」タブを示しています。

blockchain_chaincode_execute.pngの説明が続きます
図blockchain_chaincode_execute.pngの説明

REST APIを使用してOracle Blockchain Platformスマート・コントラクト・メソッドを起動するには、POSTメソッドを使用し、2つの部分を連結したURLを指定します。最初の部分は、Oracle Blockchain PlatformのRESTプロキシ・エンドポイントです。このエンドポイントは、Oracle Blockchain Platformコンソールの「ノード」タブから取得できます。

2番目の部分は、Transaction APIを使用してトランザクションを起動する特定のURIです。POSTリクエストを送信するには、ドキュメンテーションを参照してください。2つの部分は、ここに示すような完全な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ガイドのOAuth 2.0アクセス・トークン・ベース認証の使用を参照してください。
  • 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チェーンコードの管理に使用できるメソッドの詳細は、「さらに探索」の項を参照してください。

APIは、Oracle Content Management WebフックとVisual Builder Cloud Service (VBCS)上に構築されたWebアプリケーションから直接コールすることも、外部でホストされているマーケットプレイスWebアプリケーションで使用するためにAPIゲートウェイを再マップしてラップすることもできます。NFTを作成(最小)する場合、指定する必要があるパラメータの1つはtoken_uriで、デジタル・オブジェクトを表すJSONファイルを指すIPFS URIであることに注意してください。

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

次の手順では、IPFSデスクトップを使用してトークン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元帳でメタデータを保持して簡単に取得できます。

Oracle Content Management (OCM)を使用して作成されたNFTの場合、OCM Webフックを使用してAPIゲートウェイを使用してOracle Functionsをコールし、アーキテクチャ図に示すように、REST APIの呼出しをOracle Functionsに配置します。また、ブロックチェーンREST APIは、マーケットプレイスのWebアプリケーションから直接呼び出すことも、APIゲートウェイ・マッピングを使用してユーザー生成のコンテンツをサポートすることもできます。Marketplace取引機能では、支払を処理するためのカスタム・メソッドを起動してから、buyWithTokensbuyWithDirectPaymentサンプル・メソッドなどのNFT所有権の転送をトリガーできます。