このチュートリアルでは、複合的なNFTを作成、デプロイ、移動させることで、リソースが他のリソースを所有する方法を説明します。
ⓘ ACTION(操作)
このチュートリアルには、サンプルコードのみが含まれています。関連するプレイグラウンドプロジェクトはありません。ただし、このコードをコピーしてプレイグラウンドに貼り付け、テストすることは可能です。
リソースを所有するリソースは、ブロックチェーンとスマートコントラクトの世界では強力な機能です。
このチュートリアルを進める前に、「Getting Started」、「Hello, World!」、および「Resources」の手順に従って、プレイグラウンドとCadenceについて学んでください。
Resources Owning Resources
非代替トークン(Non-Fungible Tokens)で話題になっているNFTコレクションは、他のリソースを所有するリソースの一例です。NFTコレクションというリソースがあり、その中に保存されているNFTリソースを所有しています。所有者と参照(reference)を持つ人は誰でも、これらのリソースを移動させることができます(補足: 例としてマーケットプレイスのようなスマートコントラクトに所有者が参照を渡しているケースがあります。)が、それらがコレクション内に存在する限り、それらはコレクションに属しており、コレクションで定義されたコードがリソースを最終的に制御します。
コレクションが移動または破棄された場合、その中にあるすべてのNFTも一緒に移動または破棄されます。
コレクションの所有者がコレクションのリソース全体を他のユーザーのアカウントに転送した場合、トークンはすべて他のユーザーのアカウントに移動します。トークンは元の所有者のアカウントには残りません。これは、1ドル紙幣ではなく財布を誰かに手渡すようなものです。一般的ではありませんが、確かに可能です。
他のリソースに格納されているリソースに対しては参照を作成することはできません。 所有するリソースがそれを制御し、したがって、格納されたリソースに対する外部からの呼び出しのアクセス権限を制御します。
Example
NFTコレクションは、リソースが他のリソースを所有できることを示す単純な例ですが、より革新的で強力なバージョンを作成することも可能です。
CryptoKitties(およびイーサリアム・ブロックチェーン上のその他のアプリケーション)の重要な特徴は、既存のアプリケーションの周りに、あらゆる開発者が新しい体験を作り出せることです。オリジナルのスマートコントラクトには、CryptoKittyのアクセサリー(帽子など)に対する具体的なサポートは含まれていませんでしたが、独立した開発者は、オリジナルのスマートコントラクトのKittiesが使える帽子を作ることができました。
以下は、この機能をCadenceで再現する方法の基本的な例です。
// KittyVerse.cdc
//
// The KittyVerse contract defines two types of NFTs.
// One is a KittyHat, which represents a special hat, and
// the second is the Kitty resource, which can own Kitty Hats.
//
// You can put the hats on the cats and then call a hat function
// that tips the hat and prints a fun message.
//
// This is a simple example of how Cadence supports
// extensibility for smart contracts, but the language will soon
// support even more powerful versions of this.
//
access(all) contract KittyVerse {
// KittyHat is a special resource type that represents a hat
access(all) resource KittyHat {
access(all) let id: Int
access(all) let name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
// An example of a function someone might put in their hat resource
access(all) fun tipHat(): String {
if self.name == "Cowboy Hat" {
return "Howdy Y'all"
} else if self.name == "Top Hat" {
return "Greetings, fellow aristocats!"
}
return "Hello"
}
}
// Create a new hat
access(all) fun createHat(id: Int, name: String): @KittyHat {
return <-create KittyHat(id: id, name: name)
}
access(all) resource Kitty {
access(all) let id: Int
// place where the Kitty hats are stored
access(all) var items: @{String: KittyHat}
init(newID: Int) {
self.id = newID
self.items <- {}
}
access(all) fun getKittyItems(): @{String: KittyHat} {
var other: @{String:KittyHat} <- {}
self.items <-> other
return <- other
}
access(all) fun setKittyItems(items: @{String: KittyHat}) {
var other <- items
self.items <-> other
destroy other
}
access(all) fun removeKittyItem(key: String): @KittyHat? {
var removed <- self.items.remove(key: key)
return <- removed
}
}
access(all) fun createKitty(): @Kitty {
return <-create Kitty(newID: 1)
}
}
これらの定義は、Kittyリソースが帽子を所有できることを示しています。
帽子はKittyリソースの変数に格納されています。
// place where the Kitty hats are stored
access(all) var items: @{String: KittyHat}
キティの帽子を脱がせ、個別に移動させることができます。または、帽子を所有するキティを移動させ、帽子はキティと一緒に移動します。
KittyとKittyHatを作成し、Kittyに帽子を保存し、次にそれをアカウントのストレージに保存します。
import KittyVerse from 0x06
// This transaction creates a new kitty, creates two new hats and
// puts the hats on the cat. Then it stores the kitty in account storage.
transaction {
prepare(acct: auth(SaveValue) &Account) {
// Create the Kitty object
let kitty <- KittyVerse.createKitty()
// Create the KittyHat objects
let hat1 <- KittyVerse.createHat(id: 1, name: "Cowboy Hat")
let hat2 <- KittyVerse.createHat(id: 2, name: "Top Hat")
let kittyItems <- kitty.getKittyItems()
// Put the hat on the cat!
let oldCowboyHat <- kittyItems["Cowboy Hat"] <- hat1
destroy oldCowboyHat
let oldTopHat <- kittyItems["Top Hat"] <- hat2
destroy oldTopHat
kitty.setKittyItems(items: <-kittyItems)
log("The cat has the hats")
// Store the Kitty in storage
acct.storage.save(<-kitty, to: /storage/kitty)
}
}
これで、キティとその帽子を一緒に移動させるトランザクションを実行し、キティからカウボーイハットを取り除き、キティに帽子を脱がせることができます。
import KittyVerse from 0x06
// This transaction moves a kitty out of storage, takes the cowboy hat off of the kitty,
// calls its tip hat function, and then moves it back into storage.
transaction {
prepare(acct: auth(Storage) &Account) {
// Move the Kitty out of storage, which also moves its hat along with it
let kitty <- acct.storage.load<@KittyVerse.Kitty>(from: /storage/kitty)
?? panic("Kitty doesn't exist!")
// Take the cowboy hat off the Kitty
let cowboyHat <- kitty.removeKittyItem(key: "Cowboy Hat")
?? panic("cowboy hat doesn't exist!")
// Tip the cowboy hat
log(cowboyHat.tipHat())
destroy cowboyHat
// Tip the top hat that is on the Kitty
log(kitty.items["Top Hat"]?.tipHat())
// Move the Kitty to storage, which
// also moves its hat along with it.
acct.storage.save(<-kitty, to: /storage/kitty)
}
}
このトランザクションを実行すると、次のような出力が表示されます。
> "Howdy Y'all"
> "Greetings, fellow aristocats!"
キティが動くと、その帽子も暗黙のうちに一緒に動きます。これは、帽子がキティの所有物であるためです。
Extensibility is coming
上記は、コンポーザブルリソースの簡単な例です。この例では、KittyがHatを所有できることを明示的に言わなければなりませんでした。しかし、Cadenceでは、所有リソースが所有可能性を最初から指定していなかった場合でも、開発者が分離したリソースが所有できるタイプを宣言できる、より強力なリソース拡張性を実現する方法が(補足: Cadence version1.0で)サポートされるようになりました。
この機能は「Attachment(アタッチメント)」と呼ばれ、この強力な機能について学ぶには、ドキュメントを確認してください!
Flow Playgroundで学んだことを実践してみましょう!
翻訳元->https://cadence-lang.org/docs/tutorial/resources-compose