このチュートリアルでは、ブロックチェーン フロー上でデジタル コレクション (または NFT) を収集するための Web サイトを構築する方法を学びます。すべてを実現するために、スマート コントラクト言語 Cadence と React を使用します。また、Flow、その利点、使用できる楽しいツールについても学びます。 この記事を読み終えるまでに、Flow ブロックチェーン上に独自の分散アプリケーションを作成するために必要なツールと知識が得られるでしょう。 さっそく飛び込んでみましょう! 私たちは何を構築しているのでしょうか? 私たちはデジタル コレクション用のアプリケーションを構築しています。各収集品は非代替トークン (NFT) です。 私たちのアプリではNFTを収集することができ、各アイテムは他のアイテムとは異なります。 (初心者でNFTを理解していない場合は、ここをご覧ください。) これらすべてを機能させるために、Flow の NonFungibleToken Standard を使用します。これは、これらの特別なデジタル アイテムの管理に役立つ一連のルールです (イーサリアムの ERC-721 に似ています)。 前提条件 開始する前に、システムに Flow CLI を必ずインストールしてください。まだ行っていない場合は、次の に従ってください。 インストール手順 セットアップ プロジェクトを開始する準備ができたら、まずコマンド フロー設定を入力します。 このコマンドは、プロジェクトの基盤を設定するために舞台裏で魔法を実行します。フォルダー システムが作成され、プロジェクトを構成するために flow.json というファイルがセットアップされ、すべてが整理されて準備が整っていることが確認されます。 プロジェクトの構造 プロジェクトには、 フォルダーと ファイルが含まれます。 (flow.json ファイルはプロジェクトの構成ファイルであり、自動的に維持されます。) Cadence フォルダーには次のものが含まれています。 cadence flow.json /contracts: すべての Cadence 契約が含まれます。 /scripts: すべての Cadence スクリプトを保持します。 /transactions: すべての Cadence トランザクションを保存します。 Flow NFT Standardを使用するには、以下の手順に従ってください。 ステップ 1: ファイルを作成します。 まず、 フォルダーに移動し、 フォルダーを見つけます。次に、 フォルダーを開きます。新しいファイルを作成し、 という名前を付けます。 flow-collectibles-portal cadence contracts NonFungibleToken.cdc ステップ 2: コピーして貼り付けます。 ここで、NFT 標準が含まれている という名前のリンクを開きます。そのファイルからすべてのコンテンツをコピーし、作成したばかりの新しいファイル (「NonFungibleToken.cdc」) に貼り付けます。 NonFungibleToken それでおしまい!プロジェクトの標準が正常に設定されました。 では、コードを書いてみましょう。 ただし、コーディングに入る前に、開発者はコードをどのように構造化するかについてのメンタル モデルを確立することが重要です。 トップレベルでは、コードベースは 3 つの主要コンポーネントで構成されます。 NFT: 各収集品は NFT として表されます。 コレクション:コレクションとは、特定のユーザーが所有するNFTのグループを指します。 グローバル関数と変数: これらは、スマート コントラクトのグローバル レベルで定義された関数と変数であり、特定のリソースには関連付けられません。 スマートコントラクトの構造 スマートコントラクトの基本構造 という名前の新しいファイルを 内に作成します。ここにコードを記述していきます。 Collectibles.cdc cadence/contracts 契約構造 import NonFungibleToken from "./NonFungibleToken.cdc" pub contract Collectibles: NonFungibleToken{ pub var totalSupply: UInt64 // other code will come here init(){ self.totalSupply = 0 } } コードを 1 行ずつ分解してみましょう。 まず、いわゆる「NonFungibleToken」を含めることによって、NFT を構築していることを標準化する必要があります。これは、Flow によって構築された NFT 標準であり、各 NFT スマート コントラクトに含める必要がある次の機能セットを定義します。 インポートしたら、契約を作成しましょう。これを行うには、 を使用します。新しいコントラクトを作成するたびに、同じ構文を使用します。 には、契約を任意に呼び出すことができます。ここでは、これを と呼びましょう。 pub contract [contract name] contract name Collectibles 次に、コントラクトが NonFungibleToken の特定の機能とルールに従っていることを確認したいと思います。これを行うには、「:」を使用して NonFungibleToken インターフェースを追加します。 このように ( ) `pub contract Collectibles: NonFungibleToken{}` すべてのコントラクトには 関数 。これは、コントラクトが最初にデプロイされるときに呼び出されます。これは、Solidity がコンストラクターと呼ぶものに似ています。 init() が必要です ここで、データ型 の というグローバル変数を作成しましょう。この変数は、収集品の合計を追跡します。 UInt64 totalSupply ここで、 値 で初期化します。 totalSupply 0 それでおしまい!私たちは 契約の基礎を確立しました。これからは、機能をさらに追加して、さらにエキサイティングなものにすることができます。 Collectibles 先に進む前に、コード スニペットをチェックして、Cadence で変数を定義する方法を理解してください。 リソースNFT 次のコードをスマート コントラクトに追加します。 import NonFungibleToken from "./NonFungibleToken.cdc" pub contract Collectibles: NonFungibleToken{ // above code… pub resource NFT: NonFungibleToken.INFT{ pub let id: UInt64 pub var name: String pub var image: String init(_id:UInt64, _name:String, _image:String){ self.id = _id self.name = _name self.image = _image } } // init()... } 前に見たように、コントラクトは で表される NFT 標準インターフェイスを実装しています。同様に、リソースもさまざまなリソース インターフェイスを実装できます。 pub contract Collectibles: NonFungibleToken そこで、 インターフェイスを NFT リソースに追加しましょう。これにより、リソース内に id と呼ばれるパブリック プロパティの存在が必須になります。 NFT リソースで使用する変数は次のとおりです。 NonFungibleToken.INFT NFTのIDを保持します id: NFT の名前。 name: NFTの画像URL。 image: 変数を定義した後は、必ず 関数で変数を初期化してください。 init() 次に進んで、 という名前の別のリソースを作成しましょう。 Collection Resource コレクションリソース まず、 どのように機能するかを理解する必要があります。 Collection Resources 音楽ファイルと数枚の写真をラップトップに保存する必要がある場合、どうしますか? 通常は、ローカル ドライブ (D ドライブとしましょう) に移動し、 フォルダーと フォルダーを作成します。次に、音楽ファイルと写真ファイルをコピーして、宛先フォルダーに貼り付けます。 同様に、これが Flow 上のデジタル コレクションの仕組みです。 music photos ラップトップが 、D ドライブが 、フォルダーが であると想像してください。 Flow Blockchain Account Account Storage Collection したがって、NFTを購入するためにプロジェクトと対話すると、プロジェクトはDドライブにフォルダーを作成するのと同様に、 に を作成します。 10 個の異なる NFT プロジェクトを操作すると、アカウント内に 10 個の異なるコレクションが存在することになります。 account storage collection それは、自分だけのデジタル宝物を保管、整理するための個人的なスペースを持つようなものです。 import NonFungibleToken from "./NonFungibleToken.cdc" pub contract Collectibles: NonFungibleToken{ //Above code NFT Resource… // Collection Resource pub resource Collection{ } // Below code… } 各 を保持するための 変数があります。 collection NFT Resources ownedNFTs pub resource Collection { pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} init(){ self.ownedNFTs <- {} } } リソースインターフェース Flow の インターフェイスは、他のプログラミング言語のインターフェイスに似ています。これはリソースの最上位に位置し、それを実装するリソースがインターフェイスで定義されている必要な機能を備えていることを保証します。 resource また、リソース全体へのアクセスを制限したり、アクセス修飾子に関してリソース自体よりも制限を厳しくしたりするために使用することもできます。 標準には、 、 、 、 などのいくつかのリソース インターフェイスがあります。 NonFungibleToken INFT Provider Receiver CollectionPublic これらの各インターフェイスには、それらを使用するリソースによって実装する必要がある特定の機能とフィールドがあります。 このコントラクトでは、 、 、および を使用します。これらのインターフェイスは、 、 、 、 の関数を定義します。それぞれについて詳しく説明していきます。 NonFungibleToken: Provider Receiver CollectionPublic deposit withdraw borrowNFT getIDs また、これらの関数から発行するいくつかのイベントを追加し、チュートリアルの後半で使用するいくつかの変数を宣言します。 pub contract Collectibles:NonFungibleToken{ // rest of the code… pub event ContractInitialized() pub event Withdraw(id: UInt64, from: Address?) pub event Deposit(id: UInt64, to: Address?) pub let CollectionStoragePath: StoragePath pub let CollectionPublicPath: PublicPath pub resource interface CollectionPublic{ pub fun deposit(token: @NonFungibleToken.NFT) pub fun getIDs(): [UInt64] pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT } pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic{ pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} init(){ self.ownedNFTs <- {} } } } 撤回する 次に、インターフェースに必要な 関数を作成しましょう。 withdraw() pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic{ // other code pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") emit Withdraw(id: token.id, from: self.owner?.address) return <- token } init()... } この機能を使用すると、NFT リソースをコレクションから移動できます。それであれば: : パニックになり、エラーがスローされます。 失敗 : 撤退イベントを発行し、呼び出し元にリソースを返します。 成功 その後、呼び出し元はこのリソースを使用して、自分のアカウント ストレージ内に保存できます。 デポジット ここで、 に必要な 関数を使用します。 NonFungibleToken.Receiver deposit() pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic{ // other code pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") emit Withdraw(id: token.id, from: self.owner?.address) return <- token } pub fun deposit(token: @NonFungibleToken.NFT) { let id = token.id let oldToken <- self.ownedNFTs[id] <-token destroy oldToken emit Deposit(id: id, to: self.owner?.address) } init()... } 借りてIDを取得 ここで、 と に焦点を当てましょう。 NonFungibleToken.CollectionPublic: borrowNFT() getID() pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic{ // other code pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") emit Withdraw(id: token.id, from: self.owner?.address) return <- token } pub fun deposit(token: @NonFungibleToken.NFT) { let id = token.id let oldToken <- self.ownedNFTs[id] <-token destroy oldToken emit Deposit(id: id, to: self.owner?.address) } pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT { if self.ownedNFTs[id] != nil { return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)! } panic("NFT not found in collection.") } pub fun getIDs(): [UInt64]{ return self.ownedNFTs.keys } init()... } デストラクター コレクション リソースに最後に必要なのはデストラクターです。 destroy (){ destroy self.ownedNFTs } Collection リソースには他のリソース (NFT リソース) が含まれるため、デストラクターを指定する必要があります。デストラクターは、オブジェクトが破棄されるときに実行されます。これにより、親リソースが破壊されたときにリソースが「ホームレス」のままにされることがなくなります。 NFT リソースには他のリソースが含まれていないため、デストラクターは必要ありません。 完全なコレクション リソースのソース コードを見てみましょう。 import NonFungibleToken from "./NonFungibleToken.cdc" pub contract Collectibles: NonFungibleToken{ pub var totalSupply: UInt64 pub resource NFT: NonFungibleToken.INFT{ pub let id: UInt64 pub var name: String pub var image: String init(_id:UInt64, _name:String, _image:String){ self.id = _id self.name = _name self.image = _image } } pub resource interface CollectionPublic{ pub fun deposit(token: @NonFungibleToken.NFT) pub fun getIDs(): [UInt64] pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT } pub event ContractInitialized() pub event Withdraw(id: UInt64, from: Address?) pub event Deposit(id: UInt64, to: Address?) pub let CollectionStoragePath: StoragePath pub let CollectionPublicPath: PublicPath pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic{ pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} init(){ self.ownedNFTs <- {} } destroy (){ destroy self.ownedNFTs } pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") emit Withdraw(id: token.id, from: self.owner?.address) return <- token } pub fun deposit(token: @NonFungibleToken.NFT) { let id = token.id let oldToken <- self.ownedNFTs[id] <-token destroy oldToken emit Deposit(id: id, to: self.owner?.address) } pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT { if self.ownedNFTs[id] != nil { return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)! } panic("NFT not found in collection.") } pub fun getIDs(): [UInt64]{ return self.ownedNFTs.keys } } init(){ self.CollectionPublicPath = /public/NFTCollection self.CollectionStoragePath = /storage/NFTCollection self.totalSupply = 0 emit ContractInitialized() } } これで、すべてのリソースが完成しました。次に、グローバル関数を見ていきます。 グローバル機能 グローバル関数は、スマート コントラクトのグローバル レベルで定義される関数であり、リソースの一部ではないことを意味します。これらは一般からアクセスして呼び出すことができ、スマート コントラクトの中核機能を一般に公開します。 : この関数は、空の を呼び出し元アカウント ストレージに初期化します。 createEmptyCollection Collectibles.Collection : この便利な関数は、アカウントに リソースが既に存在するかどうかを確認するのに役立ちます。 checkCollection collection : この機能は、誰でも NFT を作成できるため、非常にクールです。 mintNFT // pub resource Collection… pub fun createEmptyCollection(): @Collection{ return <- create Collection() } pub fun checkCollection(_addr: Address): Bool{ return getAccount(_addr) .capabilities.get<&{Collectibles.CollectionPublic}> (Collectibles.CollectionPublicPath)! .check() } pub fun mintNFT(name:String, image:String): @NFT{ Collectibles.totalSupply = Collectibles.totalSupply + 1 let nftId = Collectibles.totalSupply var newNFT <- create NFT(_id:nftId, _name:name, _image:image) return <- newNFT } init()... スマートコントラクトのまとめ そしてついに、すべてが整ったので、スマート コントラクトの作成が完了しました。 最終的なコードを見てください。 ここで 次に、ユーザーが Flow ブロックチェーンにデプロイされたスマート コントラクトとどのように対話するかを見てみましょう。 Flow ブロックチェーンを操作するには 2 つの手順があります。 トランザクションを実行して状態を変更します。 スクリプトを実行してブロックチェーンをクエリします。 トランザクションを実行して状態を変更する トランザクションは、フロー状態を更新するためにスマート コントラクトと対話する一連の命令を含む、暗号化された署名付きデータです。簡単に言えば、これはブロックチェーン上のデータを変更する関数呼び出しのようなものです。通常、トランザクションにはある程度のコストがかかりますが、そのコストは使用しているブロックチェーンによって異なります。 トランザクションには、複数のオプションのフェーズ ( 、 、 フェーズ、および フェーズ) が含まれます。 prepare pre execute post 。各フェーズには目的があります。最も重要な 2 つのフェーズは、 と です。 詳細については、トランザクションに関する Cadence リファレンス ドキュメントを参照してください prepare execute : このフェーズは、署名者のアカウント内のデータと情報にアクセスするために使用されます (AuthAccount タイプによって許可されます)。 Prepare Phase : このフェーズはアクションを実行するために使用されます。 Execute Phase 次に、プロジェクトのトランザクションを作成しましょう。 以下の手順に従って、プロジェクト フォルダーにトランザクションを作成します。 ステップ 1: ファイルを作成します。 まず、プロジェクト フォルダーに移動し、 フォルダーを開きます。その中に フォルダーを開き、 および という名前の新しいファイルを作成します。 cadence transaction Create_Collection.cdc mint_nft.cdc ステップ 2: コレクション作成トランザクション コードを追加します。 import Collectibles from "../contracts/Collectibles.cdc" transaction { prepare(signer: AuthAccount) { if signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) == nil { let collection <- Collectibles.createEmptyCollection() signer.save(<-collection, to: Collectibles.CollectionStoragePath) let cap = signer.capabilities.storage.issue<&{Collectibles.CollectionPublic}>(Collectibles.CollectionStoragePath) signer.capabilities.publish( cap, at: Collectibles.CollectionPublicPath) } } } このコードを 1 行ずつ分解してみましょう。 このトランザクションは、Collectibles スマート コントラクトと対話します。次に、指定されたストレージ パス から Collection リソースへの参照を借用することで、送信者 (署名者) のアカウントに Collection リソースが保存されているかどうかを確認します。参照が nil の場合、署名者がまだコレクションを持っていないことを意味します。 Collectibles.CollectionStoragePath 署名者がコレクションを持たない場合は、 関数を呼び出して空のコレクションを作成します。 createEmptyCollection() 空のコレクションを作成した後、それを指定されたストレージ パス の下の署名者のアカウントに配置します。 Collectibles.CollectionStoragePath これにより、 を使用して、署名者のアカウントと新しく作成されたコレクションの間にリンクが確立されます。 link() ステップ 3: Mint NFT トランザクション コードを追加します。 import NonFungibleToken from "../contracts/NonFungibleToken.cdc" import Collectibles from "../contracts/Collectibles.cdc" transaction(name:String, image:String){ let receiverCollectionRef: &{NonFungibleToken.CollectionPublic} prepare(signer:AuthAccount){ self.receiverCollectionRef = signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) ?? panic("could not borrow Collection reference") } execute{ let nft <- Collectibles.mintNFT(name:name, image:image) self.receiverCollectionRef.deposit(token: <-nft) } } このコードを 1 行ずつ分解してみましょう。 まず、 と をインポートします。 NonFungibleToken Collectibles contract この行は、新しいトランザクションを定義します。これは 2 つの引数、name と image を受け取り、どちらも String 型です。これらの引数は、鋳造される NFT の名前とイメージを渡すために使用されます。 transaction(name: String, image: String) この行は、新しい変数 これは、 型の NFT のパブリック コレクションへの参照です。この参照は、新しく鋳造された NFT を預けるコレクションと対話するために使用されます。 let receiverCollectionRef: &{NonFungibleToken.CollectionPublic} receiverCollectionRef. NonFungibleToken.CollectionPublic この行は、トランザクションの前に実行される準備ブロックを開始します。これは、 型の引数署名者を受け取ります。 トランザクションの署名者のアカウントを表します。 prepare(signer: AuthAccount) AuthAccount AuthAccount これは、準備ブロック内の署名者のストレージから への参照を借用します。これは、borrow 関数を使用してコレクションへの参照にアクセスし、それを 変数に保存します。 Collectibles.Collection receiverCollectionRef 参照が見つからない場合 (たとえば、コレクションが署名者のストレージに存在しない場合)、「コレクション参照を借用できませんでした」というエラー メッセージがスローされます。 ブロックには、トランザクションの主な実行ロジックが含まれています。このブロック内のコードは、 ブロックが正常に完了した後に実行されます。 execute prepare ブロック内で、この行は指定された name および image 引数を使用して コントラクトから 関数を呼び出します。この関数は、指定された名前とイメージで新しい NFT を作成することが期待されます。 記号は、NFT が移動可能なオブジェクト (リソース) として受信されていることを示します。 nft <- Collectibles.mintNFT(_name: name, image: image) execute Collectibles mintNFT <- この行は、新しく作成された NFT を指定されたコレクションにデポジットします。これは、 のデポジット関数を使用して、NFT の所有権をトランザクションの実行アカウントからコレクションに移します。ここの 記号は、NFT が プロセス中にリソースとして移動されていることも示します。 self.receiverCollectionRef.deposit(token: <-nft) receiverCollectionRef <- deposit スクリプトを実行してブロックチェーンをクエリする スクリプトを使用して、ブロックチェーンからデータを表示または読み取ります。スクリプトは無料で、署名の必要はありません。 以下の手順に従って、プロジェクト フォルダーにスクリプトを作成します。 ステップ 1: ファイルを作成します。 まず、プロジェクト フォルダーに移動し、 フォルダーを開きます。その中にある フォルダーを開き、 という名前の新しいファイルを作成します。 cadence script view_nft.cdc ステップ 2: NFT スクリプトを表示する import NonFungibleToken from "../contracts/NonFungibleToken.cdc" import Collectibles from "../contracts/Collectibles.cdc" pub fun main(user: Address, id: UInt64): &NonFungibleToken.NFT? { let collectionCap= getAccount(user).capabilities .get<&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.borrowNFT(id: id) } このコードを 1 行ずつ分解してみましょう。 まず、 と コントラクトをインポートします。 NonFungibleToken Collectibles この行は、main という名前のパブリック関数であるスクリプトのエントリ ポイントを定義します。この関数は 2 つのパラメータを取ります。 pub fun main(acctAddress: Address, id: UInt64): &NonFungibleToken.NFT? : Flow ブロックチェーン上のアカウントのアドレスを表す タイプのパラメーター。 acctAddress Address : コレクション内の NFT の一意の識別子を表す タイプのパラメーター。 id UInt64 次に、 を使用して、指定された の 機能を取得します。ケイパビリティは、その機能とデータへのアクセスを許可するリソースへの参照です。この場合、 リソース タイプの機能を取得しています。 getCapability acctAddress Collectibles.Collection Collectibles.Collection 次に、 関数を使用して から NFT を借用します。 関数は、コレクション内の NFT の一意の識別子である パラメーターを受け取ります。機能の 関数を使用すると、リソース データを読み取ることができます。 borrowNFT collectionRef borrowNFT id borrow 最後に、関数から NFT を返します。 ステップ 3: テストネットの展開 次に、スマート コントラクトを Flow テストネットにデプロイします。 1. Flowアカウントを設定します。 ターミナルで次のコマンドを実行して、フロー アカウントを生成します。 flow keys generate 公開キーと秘密キーを必ず書き留めてください。 次に向かうのは、 、キーに基づいて新しいアドレスを作成し、いくつかのテスト トークンをアカウントに入金します。アカウントを作成するには、次の手順を実行します。 フローフォーセット 指定された入力フィールドに公開キーを貼り付けます。 署名とハッシュ アルゴリズムをデフォルトに設定したままにします。 キャプチャを完了します。 「アカウントの作成」をクリックします。 アカウントを設定した後、1,000 個のテスト フロー トークンを含む新しいフロー アドレスを含むダイアログを受け取ります。 。 今後使用できるようにアドレスをコピーします 2. プロジェクトを構成します。 次に、プロジェクトを構成しましょう。最初にプロジェクトをセットアップすると、 ファイルが作成されました。 flow.json これは Flow CLI の構成ファイルであり、Flow CLI が実行できるアクションの構成を定義します。これは、イーサリアムの とほぼ同等であると考えてください。 hardhat.config.js 次に、コード エディターを開き、以下のコードをコピーして ファイルに貼り付けます。 flow.json { "contracts": { "Collectibles": "./cadence/contracts/Collectibles.cdc", "NonFungibleToken": { "source": "./cadence/contracts/NonFungibleToken.cdc", "aliases": { "testnet": "0x631e88ae7f1d7c20" } } }, "networks": { "testnet": "access.devnet.nodes.onflow.org:9000" }, "accounts": { "testnet-account": { "address": "ENTER YOUR ADDRESS FROM FAUCET HERE", "key": "ENTER YOUR GENERATED PRIVATE KEY HERE" } }, "deployments": { "testnet": { "testnet-account": [ "Collectibles" ] } } } コピーアンドペースト。 生成された秘密キーをコード内の場所 (キー: 「ENTER YOUR GENERATED PRIVATE KEY HERE」) に貼り付けます。 実行する。 次に、テストネット上でコードを実行します。ターミナルに移動し、次のコードを実行します。 flow project deploy --network testnet 5. 確認を待ちます。 トランザクションを送信すると、トランザクション ID が届きます。テストネット上でトランザクションが確認され、スマート コントラクトが正常にデプロイされたことが示されるまで待ちます。 デプロイされた契約を 確認してください。 ここで で完全なコードを確認してください。 GitHub 最終的な感想とおめでとうございます! おめでとう!これで、Flow ブロックチェーン上に収集品ポータルが構築され、テストネットにデプロイされました。次は何ですか?これで、このシリーズのパート 2 で説明するフロントエンドの構築に取り組むことができます。 本当に素晴らしい一日をお過ごしください! でも公開されています ここ