Previous << Attachments
Next >> Contract Updatability
スマートコントラクトは、アカウントのcontractストレージ領域に格納される型定義、データ(その状態(state))、コード(その機能(functions))の集合です。
スマートコントラクトは、これらの型のすべての複合型インターフェイスを定義する場所です。したがって、これらの型のいずれかのオブジェクトは、配備されたCadenceコントラクトで定義されていないと存在できません。
スマートコントラクトは、アカウントで署名した状態(authorized accounts)のcontracts
オブジェクトを使用して、アカウントにデプロイしたり、更新したり、アカウントから削除したりすることができます。これらの操作の詳細については、account contractページを参照してください。
Contractsはタイプ(types)です。複合タイプ(composite types)と似ていますが、構造体やリソースとは異なる方法で保存され、リソースや構造体のように値として使用したり、コピーや移動を行うことはできません。
スマートコントラクトはアカウントのcontractストレージエリアに保存され、アカウントのオーナーが特別なコマンドを使用して追加、更新、または削除することのみが可能です。
スマートコントラクトはcontract
キーワードを使用して宣言します。キーワードの後にコントラクトの名前を続けます。
access(all)
contract SomeContract {
// ...
}
スマートコントラクトは互いにネストすることはできません。
access(all)
contract Invalid {
/* Invalid(無効): Contracts cannot be nested in any other type. */
access(all)
contract Nested {
// ...
}
}
最も単純なスマートコントラクトの1つは、状態(state)フィールド、関数(function)、およびそのフィールドを初期化する初期化子(init関数)を持つものだけでしょう。
access(all)
contract HelloWorld {
/* Declare a stored state field in HelloWorld */
access(all)
let greeting: String
/* Declare a function that can be called by anyone
* who imports the contract
*/
access(all)
fun hello(): String {
return self.greeting
}
init() {
self.greeting = "Hello World!"
}
}
トランザクションやその他のスマートコントラクトは、トランザクションまたはスマートコントラクトの定義の冒頭でインポートすることにより、スマートコントラクトと相互に作用することができます。
上記のスマートコントラクトのhello
関数は、そのスマートコントラクトをデプロイしたアカウントからインポートし、インポートしたオブジェクトを使用してhello関数を呼び出すことで、誰でも呼び出すことができます。
import HelloWorld from 0x42
/* Invalid: The contract does not know where hello comes from */
log(hello())
/* This becomes Error */
/* Valid: Using the imported contract object to call the hello function */
log(HelloWorld.hello())
/* prints "Hello World!" */
/* Valid: Using the imported contract object to read the greeting field. */
log(HelloWorld.greeting)
/* prints "Hello World!" */
/* Invalid: Cannot call the init function after the contract has been created. */
HelloWorld.init()
/* Error */
アカウントごとにスマートコントラクトをいくつでも作成でき、任意の量のデータを追加できます。つまり、スマートコントラクトには任意の数のフィールド、関数、および型定義を追加できますが、それらはスマートコントラクト内に存在する必要があり、別のトップレベル定義内に存在することはできません。
/* Invalid: Top-level declarations are restricted to only be contracts
* or contract interfaces. Therefore, all of these would be invalid
* if they were deployed to the account contract storage and
* the deployment would be rejected.
*/
access(all)
resource Vault {}
access(all)
struct Hat {}
access(all)
fun helloWorld(): String {}
let num: Int
スマートコントラクトのもう一つの重要な特徴は、スマートコントラクトで宣言されたリソースやイベントのインスタンスは、同じスマートコントラクトで宣言された関数または型の中でしか作成/発行できないことです。
スマートコントラクト外でリソースやイベントのインスタンスを作成することはできません。
以下のスマートコントラクトでは、リソースインターフェースReceiver
と、そのインターフェースを実装するリソースVault
を定義しています。以下の記述例では、このリソースを作成する箇所がありません。そのためこのスマートコントラクトおよびリソースはどこでも使用することができません。
// Valid
access(all)
contract FungibleToken {
access(all)
resource interface Receiver {
access(all)
balance: Int
access(all)
fun deposit(from: @{Receiver}) {
pre {
from.balance > 0:
"Deposit balance needs to be positive!"
}
post {
self.balance == before(self.balance) + before(from.balance):
"Incorrect amount removed"
}
}
}
access(all)
resource Vault: Receiver {
/* keeps track of the total balance of the accounts tokens */
access(all)
var balance: Int
init(balance: Int) {
self.balance = balance
}
/* withdraw subtracts amount from the vaults balance and
returns a vault object with the subtracted balance */
access(all)
fun withdraw(amount: Int): @Vault {
self.balance = self.balance - amount
return <-create Vault(balance: amount)
}
/* deposit takes a vault object as a parameter and adds
* its balance to the balance of the Account's vault, then
* destroys the sent vault because its balance has been consumed
*/
access(all)
fun deposit(from: @{Receiver}) {
self.balance = self.balance + from.balance
destroy from
}
}
}
ユーザーがVault
型のインスタンスを作成するトランザクションを実行しようとした場合、FungibleToken
スマートコントラクト内のコードのみが新しいVault
を作成できるため、型チェックでエラーが発生します。
import FungibleToken from 0x42
/* Invalid: Cannot create an instance of the `Vault` type outside
* of the contract that defines `Vault`
*/
let newVault <- create FungibleToken.Vault(balance: 10)
Account access
スマートコントラクトは、デプロイされたアカウントにアクセスできます。スマートコントラクトには、account
という暗黙的なフィールドがあり、スマートコントラクト内でのみアクセスできます。
let account: auth(Storage, Keys, Contracts, Inbox, Capabilities) &Account
アカウント参照は完全に権限が与えられているため、アカウントのストレージ、キー、デプロイされたスマートコントラクトなどへのアクセスを許可します。
例えば、これはスマートコントラクトがデプロイによって初期化された(init関数が呼ばれた)際、アカウントのストレージに書き込む能力をスマートコントラクトに与えるものです。
init(balance: Int) {
self.account.storage.save(
<-create Vault(balance: 1000),
to: /storage/initialVault
)
}
Contract interfaces
複合型(composite types)と同様に、スマートコントラクトにも、その動作(behavior)、型、および型の動作に関するルールを指定するインターフェイスを定義することができます。コントラクトインターフェースはグローバルに宣言する必要があります。宣言は他の型の中にネストさせることはできません。
コントラクトインターフェースは(イベント以外の)具体的な型を宣言することはできませんが、インターフェースを宣言することはできます。コントラクトインターフェースがインターフェース型を宣言する場合、それを実装するスマートコントラクト内ではそのインターフェースを定義する必要はありません。{ContractInterfaceName}.{NestedInterfaceName}
と記述することで、ネストされたインターフェースを参照することができます。
/* Declare a contract interface that declares an interface and a resource
* that needs to implement that interface in the contract implementation.
*/
access(all)
contract interface InterfaceExample {
/* Implementations do not need to declare this
* They refer to it as InterfaceExample.NestedInterface
*/
access(all)
resource interface NestedInterface {}
/* Implementations must declare this type */
access(all)
resource Composite: NestedInterface {}
}
access(all)
contract ExampleContract: InterfaceExample {
/* The contract doesn't need to redeclare the `NestedInterface` interface
because it is already declared in the contract interface */
/* The resource has to refer to the resource interface using the name
* of the contract interface to access it
*/
access(all)
resource Composite: InterfaceExample.NestedInterface {
}
}
翻訳元
Flow BlockchainのCadence version1.0ドキュメント (Contracts)