Cadence触りながら学ぶ雑メモ
コントラクトにpublicで設定したフィールドは誰でも参照できる
pub contract User {
pub let name: String // publicに公開
import "User"
pub fun main(): String {
let name = User.name
log("user name is ".concat(name))
}
当然publicに公開した関数もだれでも呼び出せる
pub fun getName(): String {
return self.name
}
privateなフィールドをコントラクトに定義してユーザー本人しか参照できないようにする。
pub contract User {
pub let name: String // publicに公開
access(self) let age: UInt8 // 年齢は公開したくない
普通にprivateで宣言してもできなそう?なのでCapabilityの使用を検討。
pub contract User {
pub let userPublicPath: PublicPath
pub let userPrivatePath: PrivatePath
pub let userStoragePath: StoragePath
init() {
self.userPublicPath = /public/userResource
self.userPrivatePath = /private/userResource
self.userStoragePath = /storage/userResource
self.account.save(<- create UserResource(name: "user", age: 32), to: self.userStoragePath)
self.account.link<&UserResource>(self.userPrivatePath, target: self.userStoragePath)
}
pub resource UserResource {
pub let name: String
pub let age: UInt8
init(name: String, age: UInt8) {
self.name = name
self.age = age
}
}
}
コントラクトのフィールドではなくてリソースを初期化時にコントラクトアドレスのストレージに保存しておく。ストレージのリソースをloadするのは処理的に大袈裟なので参照をprivateパスに保存しておく。
privateパスなのでアカウント本人しか参照できない。下記のようにスクリプトでコントラクトアドレスのAutuAddressを取得すれば、privateパスの参照を取り出して値を読める。
import "User"
pub fun main(): UInt8 {
let acc = getAuthAccount(0x120e725050340cab)
let user = acc.getCapability<&User.UserResource>(User.userPrivatePath)
.borrow() ?? panic("")
log(user.age)
return user.age
}
コントラクトのストレージに記録するよりもリソースにUser情報をもたせアカウントのストレージに格納するようにしてみる。コントラクトにIDフィールドを追加してユーザーが作成されるごとにインクリメントされるようにする。リソースのフィールドの参照にはinterfaceを作成して制限をかけれるようにする。
pub contract UserCreator {
pub let userPublicPath: PublicPath
pub let userPrivatePath: PrivatePath
pub let userStoragePath: StoragePath
pub var id: UInt64
init() {
self.userPublicPath = /public/user
self.userPrivatePath = /private/user
self.userStoragePath = /storage/user
self.id = 1
}
pub resource interface GetName {
pub fun getName(): String
}
pub resource interface GetAge {
pub fun getAge(): UInt8
}
pub resource User: GetName, GetAge {
pub let name: String
pub let age: UInt8
init(name: String, age: UInt8) {
self.name = name
self.age = age
}
pub fun getName(): String {
return self.name
}
pub fun getAge(): UInt8 {
return self.age
}
}
pub fun createNewUser(name: String, age: UInt8): @User {
self.id = self.id + 1
return <- create User(name: name, age: age)
}
}
ストレージの保存するのでgetAuthAccount()で認証アカウントを取得して、リソースを保存する。interfaceで制限をかけた参照を作成。
import "UserCreator"
pub fun main(): UInt8 {
// 新規でユーザーを作成してストレージに保存する
let acc = getAuthAccount(0xf3fcd2c1a78f5eee)
let user <- UserCreator.createNewUser(name: "user", age: 32)
acc.save(<-user, to: UserCreator.userStoragePath)
// 参照を作成
let onlyNamePath = /public/userName
// 名前だけ参照できる参照
acc.link<&UserCreator.User{UserCreator.GetName}>(onlyNamePath, target: UserCreator.userStoragePath)
// 年齢も参照できる参照
acc.link<&UserCreator.User{UserCreator.GetName, UserCreator.GetAge}>(UserCreator.userPublicPath, target: UserCreator.userStoragePath)
// 参照を取得
let userRef = acc.getCapability<&UserCreator.User{UserCreator.GetName, UserCreator.GetAge}>(UserCreator.userPublicPath)
.borrow() ?? panic("can not borrow User reference!")
let userNameOnlyRef = acc.getCapability<&UserCreator.User{UserCreator.GetName}>(onlyNamePath)
.borrow() ?? panic("can not borrow User name only reference!")
log(userRef.getName())
log(userRef.getAge())
// フィールド参照はできない
// userRef.name
// userRef.age
log(userNameOnlyRef.getName())
// userNameOnlyRef.getAge() これは呼べない
return userRef.getAge()
}
linkを作るときはinterfaceで制限を可能な限りかけておくのがよさそう。
Pathは/public/path1/path2
のように深いパスは書けないよう。これパスが衝突したりしないのだそうか??
scriptでgetAuthAccountしてストレージにデータを保存しようとしてもストレージに保存されていない。script内でgetAuthAccountした処理は処理が終わった後に破棄されるっぽい、たしか。
link作る時とgetCapabilityするときにinterfaceの制限が一致していないと参照が取れないので結構はまった
Testライブラリのインポートの仕方がわからない。