# トランザクション(Transaction)の概要

## **トランザクションの概要**

### トランザクションコード

`transaction`から始まるCadence言語で書かれた実行メソッドです。スマートコントラクトをimportしそのスマートコントラクトのメソッドを実行することができます。スマートコントラクトのメソッドは`access(all)`メソッドであれば誰でも呼び出せます。

**例**

```typescript
  const txId = await fcl.mutate({
      cadence: `
          import CookStocker from 0xb576a3926d239682

          transaction(id: String, add: [String], remove: [String]) {
              prepare(signer: &Account) {
                  CookStocker.setCookStockerInfo(id: id, add: add, remove: remove)
              }
              execute {
                  log("success")
              }
          }
      `,
      args: (arg, t) => [
          arg(id.toString(), t.String),
          arg(add, t.Array(t.String)),
          arg(remove, t.Array(t.String)),
      ],
```

トランザクションは値を返すことができません。トランザクションはトランザクションIDを返します。このトランザクションIDを使用してトランザクション結果を確認します。

**例**

```typescript
fcl.tx(txId).subscribe((res: any) => {
  if (res.status === 4 && !res.errorMessage) {
    isProcessing = false; // ローディングを停止
    syncBalance(); // 残高を更新
    changeScene(); // 次の処理へ
  } else if (res.errorMessage) {
    isProcessing = false;
    alert(res.errorMessage); // トランザクションがpanicで出力した内容もしくは残高不足などのエラーメッセージが含まれます。
  }
});
```

* * *

### `prepare`ブロック

```typescript
prepare(signer: &Account) {
```

`prepare`ブロックの引数にはトランザクションに署名したアカウントの数だけそのアカウントの情報が渡されます。

* * *

### `Entitlement`

エンタイトルメントと呼びます。「権利」という意味になり、スマートコントラクトの中で宣言することでトランザクションコードでも使用可能になります。

**例**

```typescript
import "FlowToken"
import "FungibleToken"

transaction(to: Address) {
    let sentVault: @{FungibleToken.Vault}

    prepare(signer: auth(BorrowValue) &Account) {
        // Hardcoded amount: 5.0
        let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(
            from: /storage/flowTokenVault
        ) ?? panic("Could not borrow reference to the owner's Vault!")

        self.sentVault <- vaultRef.withdraw(amount: 5.0)
    }

    execute {
        let receiverRef = getAccount(to)
            .capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
            .borrow()
            ?? panic("Could not borrow receiver reference to the recipient's Vault!")

        receiverRef.deposit(from: <-self.sentVault)
    }
}
```

これは署名したアカウントから`5.0`FLOWを引き出して`transaction`ブロックの引数の`to`アドレスに対して振り込んでいます。

ごく一般的な送金処理です。

`FungibleToken`コントラクトの中などで宣言された`Entitlement`はトランザクションコード内で`auth()`で囲んで使用します。`FungibleToken.Withdraw`がEntitlementです。`BorrowValue`はストレージに対するEntitlementです。

どうしても慣れる方が早いので「習うより慣れろ」、という学習スタイルになります。

上記のコードはもっと短くできます。

```typescript
import "FlowToken"
import "FungibleToken"

transaction(to: Address) {
    prepare(signer: auth(BorrowValue) &Account) {
        // Hardcoded amount: 5.0
        let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(
            from: /storage/flowTokenVault)
            ?? panic("Could not borrow reference to the owner's Vault")
        let sentVault <- vaultRef.withdraw(amount: 5.0)

        let receiverRef = getAccount(to)
            .capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
            .borrow()
            ?? panic("Could not borrow receiver reference to the recipient's Vault")
        receiverRef.deposit(from: <-sentVault)
    }
}
```

`execute`ブロックは署名したアカウントの情報が引数に渡されません。そのため、`prepare`ブロックに必要な情報が全て揃っているため、ここに必要なロジックを書くだけでトランザクションは成立します。`transaction`ブロックの外側にも変数を宣言することができ、その変数を使用して`execute`ブロックに値を渡してメイン処理を行う、というのがマナーの良い書き方になります。（しかし、コードが短い方が視認性が良いと考える人は`prepare`ブロックだけを使用します。実際一つ前のコードと見比べてください。明らかにexecuteブロック無しの方が分かりやすいです。手続き型言語のCOBOLのように利用したい場合は、transactionブロックの中で使用できるprepare ブロック、preブロック、execute ブロック、postブロック全てを使用するのもありだと思います。）

* * *

### クエリー(`query`)

トランザクションは値を返さずトランザクションIDを返しますが、クエリーは値を指定した型で返します。

```typescript
export const getInfo = async function (id) {
  const result = await fcl.query({
    cadence: `
    import CookStocker from 0x1234abcd5678efgh

    access(all) fun main(id: String): CookStocker.VegeData? {
        return CookStocker.getCookStockerInfo(id: id)
    }
    `,
    args: (arg, t) => [arg(id, t.String)],
  });
  return result;
};
```

`access(all) fun main`ブロックに対し、`:`をつけて取得する値の型を指定します。CookStocker.VegeData?というOptional型を返します。Resouce型であれば先頭に`@`がつくので自動的にVegeDataはCookStockerコントラクトのStruct(構造体)であることが分かります。

スマートコントラクトで独自の型を持つのは一般的に`Contract`型と`Struct`型と`Resource`型だけです。(`Event`や`Entitlement`も型と呼ぶ場合はそれも含みます)

ブロックチェーンにアクセスするのは主にこの２つ、トランザクションとクエリーですが、もう一つブロックチェーンにアクセスする方法があります。Eventsです。

* * *

### `Events`

**例**

```typescript
import * as fcl from "@onflow/fcl";

// Subscribe / Query events by event name and block range
const events = await fcl.events("A.0xContractAddress.ContractName.EventName")
  .fromBlockHeight(10000000)
  .toBlockHeight(10000100)
  .subscribe((event) => {
    console.log("Event received:", event);
  });
```

* * *

### `tx`

トランザクション結果をすぐに取得したい場合はEventsではなく`tx`メソッドを使用します。

```typescript
fcl.tx(txId).subscribe((res: any) => {
  if (res.status === 4 && !res.errorMessage) {
    isProcessing = false; // ローディングを停止
    syncBalance(); // 残高を更新 (中ではfcl.queryを行っています)
    changeScene(); // 次の処理へ
  } else if (res.errorMessage) {
    isProcessing = false;
    alert(res.errorMessage); // トランザクションがpanicで出力した内容もしくは残高不足などのエラーメッセージが含まれます。
  }
});
```

* * *

### クエリーとトランザクションの違い

クエリーは値を変更しようとすると`panic`が起こります。(トランザクションフィーを渡していないのでそもそも変更ができない)

１分間に複数回クエリーを発行することを実現する為にはノードを契約するという手法があります。

無料のノードを使用したい場合は以下のようにAccess Nodeを指定します。

```typescript
  let isTestnet = true;
  if (isTestnet) {
    fcl.config({
      "discovery.wallet": "https://lab.blsqui.net/authn",
      "accessNode.api": "https://rest-testnet.onflow.org",// <-無料
      "flow.network": "testnet",
      "app.detail.title": "Your IP Title here",
      "app.detail.icon": "Your IP Icon URL here",
    });
  } else {
    fcl.config({
      "discovery.wallet": "https://wallet.blsqui.net/authn",
      "accessNode.api": "https://rest-mainnet.onflow.org",// <-無料
      "flow.network": "mainnet",
      "app.detail.title": "Your IP Title here",
      "app.detail.icon": "Your IP Icon URL here",
    });
  }
```

無料の公開ノードは通常１秒間にリクエスト回数を１回までに押さえておけばエラーになりません。レートリミットを超えても１秒間に１回は正しく値を返してくれることがあります。

リソースのメソッドは`access(all)`であっても決して所有者以外は呼び出すことはできません。(リソースはアカウントストレージ内に保存されるため所有者以外はアクセスできないからです) しかし、`viewResolver` または `Capability`を使用すると呼び出すことが可能になります。

* * *

### `viewResolver`と`Capability`

あるユーザーがそのリソースに対する「ケイパビリティ（**Capability**）」を公開していない限り、スクリプトは別のユーザーのプライベートな `/storage/` からリソースを任意に読み込んだり借用したりすることはできません。そのため、リソースのデータはメソッドが`access(all)`であっても**Capability**無しでは決して所有者以外は呼び出すことはできません。

調査するには、主に2つの方法があります。

**例1:** `ViewResolver` **&** `MetadataViews`

こちらは`View`を使用する方法でコントラクト内で設定する様式が細かい為、難易度は高めです。以下は一例。

```typescript
import MetadataViews from 0x631edd8282631f7b

access(all) fun main(address: Address, id: UInt64): AnyStruct? {
    // Get public account reference
    let account = getAccount(address)

    // Borrow public capability/collection reference
    let collectionRef = account.capabilities.borrow<&{MetadataViews.ResolverCollection}>(
        /public/MyNFTCollection
    ) ?? panic("Could not borrow public collection reference")

    // Resolve the view data safely
    let view = collectionRef.borrowViewResolver(id: id)
    return view.resolveView(Type<MetadataViews.Display>())
}
```

**例2:** `Capability`**の基本的な使い方を利用する**

こちらの方が独自性のあるコードを書けます。

```swift
/* スマートコントラクト */
access(all) contract GameItem {
    // Declare Entitlement for write/privileged actions
    access(all) entitlement ManageItem

    access(all) resource Arms {
        access(all) var level: UInt64

        init() { self.level = 10 }

        // PUBLIC: Anyone holding an un-entitled capability can call this
        access(all) fun getLevel(): UInt64 {
            return self.level
        }

        // PROTECTED: Only callers holding auth(ManageItem) capability can call this
        access(ManageItem) fun levelUp() {
            self.level = self.level + 1
        }
    }
}

/* ここからトランザクション: リソースを署名者のストレージに格納 */
import GameItem from 0xCONTRACT_ADDRESS

transaction {
    prepare(signer: &Account) {
        // Save resource
        signer.storage.save(<-GameItem.createArms(), to: /storage/MyArms)

        /* Issue un-entitled capability(auth(ManageItem)がついていないので持ち主以外はlevelUpメソッドを呼び出せません) */
        let cap = signer.capabilities.storage.issue<&GameItem.Arms>(/storage/MyArms)

        /* Publish to /public/ ストレージ */
        signer.capabilities.publish(cap, at: /public/MyArms)
    }
}

/* クエリー処理 */
import GameItem from 0xCONTRACT_ADDRESS

access(all) fun main(player: Address): UInt64 {
    let ref = getAccount(player)
        .capabilities
        .borrow<&GameItem.Arms>(/public/MyArms)
        ?? panic("Could not borrow public capability")

    // Can call access(all) method directly.
    return ref.getLevel()
}
```

リソースの作成と保存は、「Struct（構造体）と Resource（リソース）の全体像」でも述べます。

* * *

### `Capability`

`Capability`は人が保持している（ストレージに保存されている）リソースのメソッドを呼び出すことができます。リソースをストレージに保存する時、ユーザーはストレージに対する保存を許可する署名を行います。その時同時に、どれぐらいまでそのリソースに対するアクセスを人(システム)に許可するかを`Entitlement`と一緒に`Capability` で設定します。`Entitlement` を付けなかった`Capability`は`access(all)`メソッドだけが他人でもアクセスできます。`Capability`を公開しなければ、`access(all)`も呼び出すことは不可能ですが、署名者はトランザクション時に自由にアクセスできますので特段不都合がある訳ではありません。ゲームであれば必要な情報は構造体(`Struct`)にまとめておく方が圧倒的に便利であり、`Capability`が必要になるのは(能力<`Capability`\>を委任したい場合など)複雑なシステムだけということが往々にしてあります。

1.  Capabilityを発行する（`auth()`がついてないので`access(all)`のメソッドのみ呼べます）
    
    ```typescript
    let cap = signer.capabilities.storage.issue<&GameItem.Arms>(/storage/MyArms)
    ```
    
2.  Capabilityを公開する
    
    ```typescript
    signer.capabilities.publish(cap, at: /public/MyArms)
    ```
    
3.  Capabilityを拝借する
    

```typescript
    let ref = getAccount(player)
        .capabilities
        .borrow<&GameItem.Arms>(/public/MyArms)
        ?? panic("Could not borrow public capability")
```

４. Capabilityを通じて呼び出す。

```typescript
return ref.getLevel()
```

`Entitlement`をつけて`Capability`を発行する時は以下のようにします。（1.と違って`auth(GameItem.ManageItem)`がついています）

```typescript
let entitledCap = signer.capabilities.storage.issue<
            auth(GameItem.ManageItem) &GameItem.Arms
        >(/storage/MyArms)
```

`Capability`は必要に迫られるまでは作成しないので最初は気にする必要はありません。

* * *

### アクセス修飾子

```typescript
access(all)         権限なくアクセス可能
access(contract)    コントラクト内からであればアクセス可能
access(self)        リソースまたはStruct内からのみアクセス可能
access(Entitlement) 宣言したEntitlementの権限を持っていればアクセス可能

例:
access(ManageItem) fun levelUp() {
  self.level = self.level + 1
}
```

* * *

### ウォレットアカウントの情報を取得する

```typescript
fcl.currentUser().subscribe((newVal: any) => {
    user = newVal;
    syncBalance();
});
```

* * *

### Flowネイティブ通貨の残高を取得する

```typescript
  // Get Player's balance
  async function syncBalance() {
    if (!user?.addr) {
      esportsBalance = '-';
      return;
    };
    const account = await fcl.account(user.addr);
    esportsBalance = String(account.balance);

    if (!esportsBalance.includes('.')) {
      const num = parseFloat(esportsBalance) / 100000000;
      esportsBalance = num.toLocaleString('en-US', {
        minimumFractionDigits: 2,
        maximumFractionDigits: 8
      });
    }
  }
```

* * *

ブロックチェーンシステムを作るに当たり、最初に覚えておかなければならないことはこれぐらいです。これだけ覚えておけば誰でも決済システム・eスポーツを作れます。
