# ちょっと高度な Hello World !

### Hello World! スマートコントラクトのデプロイ方法

バックエンドで実行するためにはウォレットが作成したアドレスでは上手くいきません。ウォレットが作成した秘密鍵はウォレットにアクセスすることでしか取得できないからです。これはバックエンドでトランザクションを行いたい時に.exeなどの実行ファイルだけでは実行できないことを意味します。

そこで、自分たちで秘密鍵とブロックチェーンアドレスを作成します。そしてそれらを使ってバックエンドからトランザクションを実行します。

* * *

まず、その前にFlow CLIというツールで `flow init` を実行したら何ができているのかを紹介します。

```json
{
	"contracts": {
	},
	"dependencies": {
		"Burner": {
			"source": "mainnet://f233dcee88fe0abe.Burner",
			"hash": "71af18e227984cd434a3ad00bb2f3618b76482842bae920ee55662c37c8bf331",
			"block_height": 149816757,
			"aliases": {
				"emulator": "f8d6e0586b0a20c7",
				"mainnet": "f233dcee88fe0abe",
				"testnet": "9a0766d93b6608b7"
			}
		},
		"FlowToken": {
			"source": "mainnet://1654653399040a61.FlowToken",
			"hash": "f82389e2412624ffa439836b00b42e6605b0c00802a4e485bc95b8930a7eac38",
			"block_height": 149816757,
			"aliases": {
				"emulator": "0ae53cb6e3f42a79",
				"mainnet": "1654653399040a61",
				"testnet": "7e60df042a9c0868"
			}
		},
		"FungibleToken": {
			"source": "mainnet://f233dcee88fe0abe.FungibleToken",
			"hash": "3b04b3191e0c5d4d9ee3ce9c671b11985d29bf2732a5721b526cc8523c536f8f",
			"block_height": 149816757,
			"aliases": {
				"emulator": "ee82856bf20e2aa6",
				"mainnet": "f233dcee88fe0abe",
				"testnet": "9a0766d93b6608b7"
			}
		},
		"FungibleTokenMetadataViews": {
			"source": "mainnet://f233dcee88fe0abe.FungibleTokenMetadataViews",
			"hash": "70477f80fd7678466c224507e9689f68f72a9e697128d5ea54d19961ec856b3c",
			"block_height": 149816757,
			"aliases": {
				"emulator": "ee82856bf20e2aa6",
				"mainnet": "f233dcee88fe0abe",
				"testnet": "9a0766d93b6608b7"
			}
		},
		"MetadataViews": {
			"source": "mainnet://1d7e57aa55817448.MetadataViews",
			"hash": "b290b7906d901882b4b62e596225fb2f10defb5eaaab4a09368f3aee0e9c18b1",
			"block_height": 149816757,
			"aliases": {
				"emulator": "f8d6e0586b0a20c7",
				"mainnet": "1d7e57aa55817448",
				"testnet": "631e88ae7f1d7c20"
			}
		},
		"NonFungibleToken": {
			"source": "mainnet://1d7e57aa55817448.NonFungibleToken",
			"hash": "a258de1abddcdb50afc929e74aca87161d0083588f6abf2b369672e64cf4a403",
			"block_height": 149816757,
			"aliases": {
				"emulator": "f8d6e0586b0a20c7",
				"mainnet": "1d7e57aa55817448",
				"testnet": "631e88ae7f1d7c20"
			}
		},
		"ViewResolver": {
			"source": "mainnet://1d7e57aa55817448.ViewResolver",
			"hash": "374a1994046bac9f6228b4843cb32393ef40554df9bd9907a702d098a2987bde",
			"block_height": 149816757,
			"aliases": {
				"emulator": "f8d6e0586b0a20c7",
				"mainnet": "1d7e57aa55817448",
				"testnet": "631e88ae7f1d7c20"
			}
		}
	},
	"networks": {
		"emulator": "127.0.0.1:3569",
		"mainnet": "access.mainnet.nodes.onflow.org:9000",
		"testnet": "access.devnet.nodes.onflow.org:9000"
	},
	"accounts": {
		"emulator-account": {
			"address": "f8d6e0586b0a20c7",
			"key": {
				"type": "file",
				"location": "emulator-account.pkey"
			}
		}
	},
	"deployments": {
		"mainnet": {
		},
		"testnet": {
		}
	}
}
```

こういう感じのJSONが`flow.json`ファイルに作成されています。このflow.jsonがスマートコントラクトをデプロイする時の全ての参照先になります。

最初は"accounts"に"emulator-account"だけあって、"testnet-account"や"mainnet-account"がありません。また、"deployments"が最初は`flow init`実行した直後は無いですが、ここは今後必要になってきます。

* * *

## アカウントを作成する

これは`Flow CLI`を使って作成します。

```shell
flow accounts create
```

実行すると、以下のような出力がされます。

```shell
Address	 0x348175517cb1c79e
Balance	 0.00200000
Keys		 1

Key 0	Public Key		 e21fd469bf39be5718fbd16f6444c1c0cef51ec40db865df8a9dc90c443b68946aa7a63d6083666109864511b6c8d589a4cc7202b3257c34c3d1a7885c3de880
		Weight			 1000
		Signature Algorithm	 ECDSA_P256
		Hash Algorithm		 SHA3_256
		Revoked 		 false
		Sequence Number 	 0
		Index 			 0

Contracts Deployed: 0
```

ECDSA\_P256アルゴリズムでPrivate KeyとPublic Key、ブロックチェーンアドレス が一度に作成されます。

* * *

flow.jsonを見てみます。

```json
	"accounts": {
		"emulator-account": {
			"address": "f8d6e0586b0a20c7",
			"key": {
				"type": "file",
				"location": "emulator-account.pkey"
			}
		},
		"testnet-account": {
			"address": "348175517cb1c79e",
			"key": {
				"type": "file",
				"location": "testnet-account.pkey"
			}
		}
	},
```

このように作成されたブロックチェーンアドレス(**348175517cb1c79e**)がすでにセットされています。`Public Key`は見当たりませんが、実はアドレスを生成したときにそこに引数として渡されています。Scanサイト(FlowScan)でこのアドレスを検索するとそこで`Public Key`を見つけることができます。

`testnet-account.pkey`というファイルが`flow.json`と同じ階層に作成されているので、それを開いてみると`Private Key`が見つかります。

同様にメインネットのアカウントも作成します。

```shell
flow accounts create
```

対象のネットワークに`mainnet`を選択すると`flow.json`は以下のようになります。

```json
	"accounts": {
		"emulator-account": {
			"address": "f8d6e0586b0a20c7",
			"key": {
				"type": "file",
				"location": "emulator-account.pkey"
			}
		},
		"mainnet-account": {
			"address": "deef3052ad5a0289",
			"key": {
				"type": "file",
				"location": "mainnet-account.pkey"
			}
		},
		"testnet-account": {
			"address": "348175517cb1c79e",
			"key": {
				"type": "file",
				"location": "testnet-account.pkey"
			}
		}
	}
```

* * *

## スマートコントラクトをデプロイする

この生成したアカウントにスマートコントラクトをデプロイすることで、好きな時にコントラクトメソッドをトランザクションで実行することができます。`Private Key`がウォレットにではなく、`testnet-account.pkey`や`mainnet-account.pkey`にあるのでこれを`.env`ファイルに貼り付けることで、どこからでもトランザクションを実行することができます。

`flow.json`ファイルと同じ階層に`cadence/contracts`フォルダがすでにあります。そこに`HelloWorld.cdc`ファイルを作成します。そこに以下を貼り付けて保存します。

```swift
access(all) contract HelloWorld {
  // Declare a public field of type String.
  //
  // All fields must be initialized in the init() function.
  access(all) var greeting: String

  // Public function that sets our friendly greeting!
  // In your own applications you may want to tighten up this access control.
  access(all) fun changeGreeting(newGreeting: String) {
    self.greeting = newGreeting
  }

  // Public function that returns our friendly greeting!
  access(all) fun hello(): String {
      return self.greeting
  }

  // The init() function is required if the contract contains any fields.
  init() {
    self.greeting = "Hello, World!"
  }
}
```

ではデプロイしましょう。

`flow.json`を開きます。

```json
	"contracts": {
		"HelloWorld": {
			"source": "./cadence/contracts/HelloWorld.cdc",
			"aliases": {
				"emulator": "f8d6e0586b0a20c7",
				"mainnet": "deef3052ad5a0289",
				"testnet": "348175517cb1c79e"
			}
		}
	},
```

`"emulator"`のアドレスは常に`"f8d6e0586b0a20c7"`です。これは変わりません。

`"HelloWorld"`の部分はコントラクト名と合わせます。`"testnet"`と`"mainnet"`はこれからデプロイするアドレスです。先ほど作成ずみのアドレスを指定します。

次に以下を記入します。

```json
	"deployments": {
		"mainnet": {
			"mainnet-account": [
				"HelloWorld"
			]
		},
		"testnet": {
			"testnet-account": [
				"HelloWorld"
			]
		}
	}
```

`"mainnet-account"`と`"testnet-account"`の部分は`flow accounts create`コマンドを実行したときに指定した名前です。そのため、人によっては`"mainnet-acc"`だったり、`"testnet-acc"`だったりします。入力したアカウント名を入れます。その下に配列を作り、"HelloWorld"をセットします。（一度に複数のコントラクトをデプロイできます）

そして以下を実行します。

```shell
flow project deploy --network testnet
```

* * *

おそらく失敗します。Σ＼(￣д￣;)ﾅﾝﾃﾞﾔﾈﾝ

出力結果はこう出ます。

```shell
❌ Failed to deploy contract HelloWorld: failed to deploy contract HelloWorld: [Error Code: 1103] error caused by: 1 error occurred:
	* storage limit check failed: [Error Code: 1103] The account with address (348175517cb1c79e) uses 1930 bytes of storage which is over its capacity (0 bytes). Capacity can be increased by adding FLOW tokens to the account.
```

この`storage limit check failed: [Error Code: 1103]`というのは開発していると必ず１回は見ることになります。これはトークンを持っていないよ、ということなので、トークンを補充します。そうすればそこからトランザクションフィーが支払われて実行が成功します。

[https://faucet.flow.com/fund-account](https://faucet.flow.com/fund-account)

にアクセスしてブロックチェーンアドレスを入力するとテストネット用のトークンを**100,000**もらえます。

再度実行するとこのように表示されるはずです。

```shell
Deploying 1 contracts for accounts: testnet-account

HelloWorld -> 0x348175517cb1c79e (a811542caa8f4ce5e897669d887b7b7c12c741e2c798e0f920b5440ab6e2f83e) 

🎉 All contracts deployed successfully
```

更新する時は以下のコマンドを実行します。

```shell
flow project deploy --network testnet --update
```

スマートコントラクトのフィールドを変更していない限り更新に成功します。（フィールド: `init`の中で初期化が必要なプロパティ）

* * *

## Hello, World!の実行

### トランザクションの実行

`/cadence/transactions`の下に`ChangeGreeting.cdc`ファイルを生成します。

そして以下を貼り付けて保存します。

```swift
import "HelloWorld"

transaction(greeting: String) {

  prepare(acct: &Account) {
    log(acct.address)
  }

  execute {
    HelloWorld.changeGreeting(newGreeting: greeting)
  }
}
```

以下のコマンドを実行します。

```shell
flow transactions send ./cadence/transactions/ChangeGreeting.cdc "Hello" --network testnet --signer testnet-account
```

以下のように結果が返されます。

![](https://cdn.hashnode.com/uploads/covers/6a54b7c15c0c654db32e149a/b3309428-fe17-434d-93ed-08cc3cedb00a.png align="center")

Go言語でもNode.jsでもできます。もちろんOS実行をするのであればFlow CLIはLinuxでもインストールできますので、Pythonでも呼び出して実行することができます。

### スクリプトの実行

`/cadence/scripts`の下にGetGreeting.cdcというファイルを作成し、以下を貼り付けて保存します。

```swift
import "HelloWorld"

access(all) fun main(): String {
  return HelloWorld.hello()
}
```

以下を実行します。

```shell
flow scripts execute ./cadence/scripts/GetGreeting.cdc --network testnet
```

以下が返されます。

```shell
Result: "Hello"
```

* * *

`Go`言語、`Node.js`で実行する時はGeminiなどの生成AIに聞きながら行うと自分で調べるより早く実装できたりします。

秘密鍵は`.env`ファイル等に入れて決してGithubにアップロードしないでください。`Private Key`さえあれば以下を実行されてあっという間に全額抜き取られます。

```javascript
const fcl = require("@onflow/fcl");
const { SHA3 } = require("sha3");
var EC = require("elliptic").ec;
var ec = new EC("p256");
require("dotenv").config();

fcl
  .config()
  .put("flow.network", "mainnet")
  .put("accessNode.api", "https://rest-mainnet.onflow.org");

// CHANGE THESE THINGS FOR YOU
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const ADDRESS = process.env.ADDRESS;
const KEY_ID = parseInt(process.env.KEY_ID);

const hash = (message) => {
  const sha = new SHA3(256);
  sha.update(Buffer.from(message, "hex"));
  return sha.digest();
};

const sign = (message) => {
  const key = ec.keyFromPrivate(Buffer.from(PRIVATE_KEY, "hex"));
  const sig = key.sign(hash(message)); // hashMsgHex -> hash
  const n = 32;
  const r = sig.r.toArrayLike(Buffer, "be", n);
  const s = sig.s.toArrayLike(Buffer, "be", n);
  return Buffer.concat([r, s]).toString("hex");
};

async function authorizationFunction(account) {
  return {
    ...account,
    tempId: `${ADDRESS}-${KEY_ID}`,
    addr: fcl.sansPrefix(ADDRESS),
    keyId: Number(KEY_ID),
    signingFunction: async (signable) => {
      return {
        addr: fcl.withPrefix(ADDRESS),
        keyId: Number(KEY_ID),
        signature: sign(signable.message),
      };
    },
  };
}

async function sendTx() {
  const addr = process.env.RECIPIENT_ADDRESS;
  const transactionId = await fcl.mutate({
    cadence: `
      import FungibleToken from 0xf233dcee88fe0abe
      import FlowToken from 0x1654653399040a61

      transaction(amount: UFix64, to: Address) {
        let vault: @FlowToken.Vault

        prepare(signer: auth(BorrowValue) &Account) {
          self.vault <- signer.storage
            .borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>(from: /storage/flowTokenVault)!
            .withdraw(amount: amount) as! @FlowToken.Vault
        }
        execute {
          getAccount(to)
            .capabilities
            .get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
            .borrow()!
            .deposit(from: <-self.vault)
        }
      }
    `,
    args: (arg, t) => [arg(100.0, t.UFix64), arg(addr, t.Address)],
    payer: authorizationFunction,
    proposer: authorizationFunction,
    authorizations: [authorizationFunction],
    limit: 999,
  });

  console.log({ transactionId });
  fcl.tx(transactionId).subscribe((res) => {
    console.log(res);
  });
}

sendTx();
```

これはNode.jsのコードですが、Goでは少し違う形になります。

ここで書いている内容は[ここ](https://qiita.com/flowcadence/items/82c4e2d91df09051b1de#signing-function)や[ここ](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/wallet-provider-spec/authorization-function.md)を参考にしてください。鍵のアルゴリズム、PRIVATE\_KEY、KEY\_IDの部分以外は同じ書き方になります。あくまで同じようなことが書いてあるな、と理解するだけで大丈夫です。大事なのは`Private Key`を決して公開しない事です。

上記のコードをsendFlow.jsのようなファイルに保存して`node ./sendFlow.js`を実行すれば、Flowトークンを好きな時に他のアカウントに送ることができます。よく使用します。
