Contracts

[Previous << Keys](https://qiita.com/flowcadence/items/19710392d7fb55545db4)
[Next >> Inbox](https://qiita.com/flowcadence/items/db88a071c732f68cc7fb)

https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a#cadenceの言語仕様を学ぶ

Accountは[contract](https://qiita.com/flowcadence/items/91a5ecb5812af1f98bef)を保存します。contractは、単なる[インタフェース](https://qiita.com/flowcadence/items/765a76e4fac1220ea7d9)である場合もあります。

Accountは、`contracts`フィールドを通じてそのInboxを公開します。このフィールドの型は`Account.Contracts`です。

<h1 id="Account.Contracts">Account.Contracts</h1>

```ruby
access(all)
struct Contracts {

    /* The names of all contracts deployed in the account. */
    access(all)
    let names: [String]

    /** Returns the deployed contract for the contract|contract interface with the given name in the account, if any.
      *
      * Returns nil if no contract|contract interface with the given name exists in the account. */
    access(all)
    view fun get(name: String): DeployedContract?

    /** Returns a reference of the given type to the contract with the given name in the account, if any.
      *
      * Returns nil if no contract with the given name exists in the account,
      * or if the contract does not conform to the given type. */
    access(all)
    view fun borrow<T: &Any>(name: String): T?

    /** Adds the given contract to the account.
      *
      * The `code` parameter is the UTF-8 encoded representation of the source code.
      * The code must contain exactly one contract or contract interface,
      * which must have the same name as the `name` parameter.
      *
      * All additional arguments that are given are passed further to the initializer
      * of the contract that is being deployed.
      *
      * The function fails if a contract|contract interface with the given name already exists in the account,
      * if the given code does not declare exactly one contract or contract interface,
      * or if the given name does not match the name of the contract|contract interface declaration in the code.
      *
      * Returns the deployed contract.
      */
    access(Contracts | AddContract)
    fun add(
        name: String,
        code: [UInt8]
    ): DeployedContract

    /** Updates the code for the contract|contract interface in the account.
      *
      * The `code` parameter is the UTF-8 encoded representation of the source code.
      * The code must contain exactly one contract or contract interface,
      * which must have the same name as the `name` parameter.
      *
      * Does **not** run the initializer of the contract|contract interface again.
      * The contract instance in the world state stays as is.
      *
      * Fails if no contract|contract interface with the given name exists in the account,
      * if the given code does not declare exactly one contract or contract interface,
      * or if the given name does not match the name of the contract|contract interface declaration in the code.
      *
      * Returns the deployed contract for the updated contract.
      */
    access(Contracts | UpdateContract)
    fun update(name: String, code: [UInt8]): DeployedContract

    /** Removes the contract|contract interface from the account which has the given name, if any.
      *
      * Returns the removed deployed contract, if any.
      *
      * Returns nil if no contract|contract interface with the given name exists in the account.
      */
    access(Contracts | RemoveContract)
    fun remove(name: String): DeployedContract?
}

entitlement Contracts

entitlement AddContract
entitlement UpdateContract
entitlement RemoveContract
```

<h1 id="Deployed-contract">Deployed contract</h1>

Accountには「デプロイされたコントラクト」、つまりスマートコントラクトのコードが保存されます。
```ruby
access(all)
struct DeployedContract {
    /* The address of the account where the contract is deployed at. */
    access(all)
    let address: Address

    /* The name of the contract. */
    access(all)
    let name: String

    /* The code of the contract. */
    access(all)
    let code: [UInt8]

    /** Returns an array of `Type` objects representing all the public type declarations in this contract
      * (e.g. structs, resources, enums).
      *
      * For example, given a contract
      * ```
      * contract Foo {
      *
      *       access(all)
      *       struct Bar {...}
      *
      *       access(all)
      *       resource Qux {...}
      * }
      *
      * ```
      * then `.publicTypes()` will return an array equivalent to the expression `[Type<Bar>(), Type<Qux>()]`
      */
    access(all)
    view fun publicTypes(): [Type]
}
```

これは、デプロイされたスマートコントラクトに関する情報のみを提供する型(type)であり、スマートコントラクトのインスタンス、すなわちスマートコントラクトのインポート結果ではないことに注意してください。

<h1 id="Getting-a-deployed-contract">Getting a deployed contract</h1>

`contracts.get`関数は、デプロイされたスマートコントラクトを取得します。
```ruby
access(all)
view fun get(name: String): DeployedContract?
```
この関数は、指定された名前を持つ[デプロイされたコントラクト](#deployed-contract)を返します。指定された名前を持つコントラクトがアカウントに存在しない場合、この関数は`nil`を返します。

例えば、アカウントに`Test`という名前のスマートコントラクトがデプロイされていると仮定すると、そのスマートコントラクトは以下のようにして取得できます。

```ruby
let account = getAccount(0x1)
let contract = account.contracts.get(name: "Test")
```


<h1 id="Borrowing-a-deployed-contract">Borrowing a deployed contract</h1>

特定の実行パスに依存したdynamicインポートを効果的に実行するために、コントラクトを「借用」することができます。（補足. オブジェクト移動はコストがあるがリファレンス取得はコストが低くなります。但しこれはLinear Typesが適用されているリソースの場合でありコントラクトの場合も同様にコストを下げられるかは分かりません...）

これは、例えば`import T from 0x1`のように静的にコントラクトをインポートする典型的なインポート文とは対照的です。

`contracts.borrow`関数は、コントラクトインスタンスへの参照を取得します。

```ruby
access(all)
view fun borrow<T: &Any>(name: String): T?
```

この関数は、アカウント上にその名前で保存されているスマートコントラクトのインスタンスへの参照を返します。そのコントラクトが存在し、かつ、指定された型`T`を持つ場合です。指定された名前のスマートコントラクトがアカウント上に存在しない場合、この関数は`nil`を返します。

例えば、`Test`という名前のスマートコントラクトが`TestInterface`インターフェースに準拠してアカウントにデプロイされていると仮定すると、スマートコントラクトインスタンスへの参照は以下のようにして取得できます。

```ruby
let account = getAccount(0x1)
let contract: &TestInterface = account.contracts.borrow<&TestInterface>(name: "Test")
```

これはimport文に似ています。

```ruby
import Test from 0x1
```

<h1 id="deploying-a-new-contract">Deploying a new contract</h1>

`contracts.add`関数は、新しいコントラクトをアカウントにデプロイします。

```ruby
access(Contracts | AddContract)
fun add(
    name: String,
    code: [UInt8],
    ... contractInitializerArguments
): DeployedContract
```

`add`関数を呼び出すには、粗粒度の`Contracts`権限（`auth(Contracts) &Account`）または細粒度の`AddContract`権限（`auth(AddContract) &Account`）で認証された参照経由でアカウントにアクセスする必要があります。

`code`パラメータは、UTF-8エンコードで表現されたソースコードです。codeには`name`パラメータと正確に同じ名前の、1つのcontractまたはコントラクトインターフェースを含んでいる必要があります

`add`関数は、呼び出し時のすべての追加引数（`contractInitializerArguments`）をコントラクトの初期化子(init)に渡します。

指定された名前のスマートコントラクトが(これからデプロイする)アカウントにすでに存在する場合、コードが宣言する(declare)コントラクトまたはコントラクトインターフェースが一つでない場合、またはnameで指定された名前がコード内のcontract宣言の名前と一致しない場合、関数はプログラムを中断します。

デプロイが成功した場合は、その関数は[デプロイされたコントラクト](#deployed-contract)を返します。

例えば、以下のコントラクトコードがデプロイされると仮定します。
```ruby
access(all)
contract Test {

    access(all)
    let message: String

    init(message: String) {
        self.message = message
    }
}
```
コントラクトは以下のようにしてデプロイすることができます：

```ruby
transaction(code: String) {
    prepare(signer: auth(AddContract) &Account) {
        signer.contracts.add(
            name: "Test",
            code: code.utf8,
            message: "I'm a new contract in an existing account"
        )
    }
}
```

<h1 id="updating-a-deployed-contract">Updating a deployed contract</h1>

`contracts.update` 関数は、既存のスマートコントラクトのコードを更新します。
```ruby
access(Contracts | UpdateContract)
fun update(name: String, code: [UInt8]): DeployedContract
```
`update`関数を呼び出すには、粗粒度の`Contracts`権限（`auth(Contracts) &Account`）または細粒度の`UpdateContract`権限（`auth(UpdateContract) &Account`）で認証された参照経由でアカウントにアクセスする必要があります。

`code`パラメータは、UTF-8でエンコードされたソースコードの表現です。コードには、正確に1つのコントラクトまたはコントラクトインターフェースを含める必要があります。このコントラクトまたはコントラクトインターフェースの名前は、`name`パラメータと同じ名前にする必要があります

アカウントに指定された名前のスマートコントラクトが存在しない場合、指定されたコードが正確に1つのコントラクトまたはコントラクトインターフェースを宣言していない場合、または指定された名前がコード内の契約宣言の名前と一致しない場合、この関数はプログラムを中断します。

更新が成功すると、この関数は[デプロイされた](#deployed-contract)スマートコントラクトを返します。

:::note warn
⚠ WARNING
`update`機能は、スマートコントラクトの初期化を再度実行**しません**。
スマートコントラクトを更新しても、コントラクトインスタンスやすでに保存されているデータを変更**しません**。スマートコントラクトの更新は、コントラクトのコードのみを変更します。
コントラクトの更新は、データの整合性を維持する方法でのみ可能です。[特定の制限](https://qiita.com/flowcadence/items/af1cfaf57838ed7f2401)が適用されます。
:::
例えば、`Test`という名前のコントラクトがすでにアカウントにデプロイされており、次のコントラクトコードで更新されるべきであると仮定します。

```ruby
access(all)
contract Test {
    
    access(all)
    let message: String

    init(message: String) {
        self.message = message
    }
}
```
スマートコントラクトは以下のようにして更新することができます。
```ruby
transaction(code: String) {
    prepare(signer: auth(UpdateContract) &Account) {
        signer.contracts.update(
            name: "Test",
            code: code
        )
    }
}
```
<h1 id="Removing-a-deployed-contract">Removing a deployed contract</h1>

`contracts.remove` 関数は、デプロイされたスマートコントラクトをアカウントから削除します。
```ruby
access(Contracts | RemoveContract)
fun remove(name: String): DeployedContract?
```
`remove`関数を呼び出すには、粗粒度の`Contracts`権限（`auth(Contracts) &Account`）または細粒度の`RemoveContract`権限（`auth(RemoveContract) &Account`）で認証された参照によるアカウントへのアクセスが必要です。

この関数は、アカウントから指定された名前を持つコントラクトを削除し、それを返します。指定された名前を持つスマートコントラクトがアカウントに存在しない場合、この関数は`nil`を返します。

例えば、`Test`という名前のコントラクトがアカウントにデプロイされていると仮定すると、スマートコントラクトは次のように削除できます。

```ruby
transaction(code: String) {
    prepare(signer: auth(RemoveContract) &Account) {
        signer.contracts.remove(name: "Test",)
    }
}
```

翻訳元

https://cadence-lang.org/docs/language/accounts/contracts

***
<b>[Previous << Keys](https://qiita.com/flowcadence/items/19710392d7fb55545db4)
</b>

[Flow blockchain / Cadence version1.0ドキュメント](https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a) (Contracts)

<b>[Next >> Inbox](https://qiita.com/flowcadence/items/db88a071c732f68cc7fb)</b>