ブロックチェーンSymbolで作る、絶対に不正出来ない割り勘スマートコントラクト

少し前に、割り勘の誘いに乗ったら預けたお金を取られたという詐欺の話を聞きました。驚くことにブロックチェーン界隈での話です。トレンドの技術を使っていたとしても、ちょっとした思い付きをコントラクトにしてすぐには実践できないのが今のブロックチェーンです。
そこで、今話題沸騰中の今年3月にローンチされた、トランザクションベースでスマートコントラクトが書けるブロックチェーンSymbolで、自分の出した額が必ず支払いに使われるスマートコントラクトを書いてみたいと思います。

「トランザクションベースのスマートコントラクト」については以下のSymbolの衝撃をご参考ください。

https://qiita.com/nem_takanobu/items/5b9388dab19d1c985d3d#3%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%AF%E3%83%88



# 概要図

最終結果を最初に紹介します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/764bf533-bf0e-07b5-2d83-620ae03adef8.png)

Alice、Bob、Carolが3XYMずつ割り勘してShopに9XYM払います。
Bob,Carolは3XYMずつをAliceに渡し、Aliceからも3XYM出した合計9XYMをAliceからShopに支払います。
Aliceが作成したトランザクションをBobとCarolが連署して実現します。

それでは体験ツアー開始です。

# 環境設定

以下のページのImportとScriptを実行しておいてください。

https://qiita.com/nem_takanobu/items/e88d5ea76cd609e76bbf



# アカウント作成

```js
alice = sym.Account.generateNewAccount(networkType);
bob = sym.Account.generateNewAccount(networkType);
carol = sym.Account.generateNewAccount(networkType);
shop = sym.Account.generateNewAccount(networkType);

console.log(alice.address.plain())
console.log(bob.address.plain())
console.log(carol.address.plain())
console.log(shop.address.plain());

```



# 入金する

以下のスクリプトを実行するとフォーセットへのURLが表示されるので、クリックして入金を行います。アドレス送金額は自動でセットされているのでそのままボタンをクリックしてください。

```js
function showFaucetURL(account,amount){
  console.log("https://testnet.symbol.tools/?recipient=" + account.address.plain() +"&amount=" + amount.toString())
}

showFaucetURL(alice,15);
showFaucetURL(bob,5);
showFaucetURL(carol,5);
```

Aliceはボンデッドトランザクションを発行する必要があるため、10XYM多めに入れておきます。

# 入金を確認する

以下のスクリプトを実行すると、各アドレスに入金できたか確認できます。およそ15秒～45秒ほどかかります。

```js
function showAccount(account){
  console.log("https://testnet.symbol.fyi/accounts/" + account.address.plain());
}

showAccount(alice);
showAccount(bob);
showAccount(carol);
```

# ウォレットに登録

BobとCarolは連署する必要があるため、ウォレットの連署機能を利用します。

ウォレットにテストネットアカウントを1つ作成し（割愛します）、
アカウント->アカウント追加->既存アカウントに秘密鍵をインポート、でbob,carolの2アカウントを作成してください。


##### 秘密鍵の求め方

```js
console.log(bob.privateKey);
console.log(carol.privateKey);
```
今回は直前に作成したので、その変数から導出します。

以下の画像を参考に、秘密鍵で生成したアカウントをインポートしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/12d4c2c2-dd4a-c784-8275-dc0c9d057c5b.png)

この手順は作成したばかりのアカウントを連署者にするために行っています。
すでにウォレットを所有している人を割り勘メンバーにする場合はこの手順は省略して大丈夫です。



# 割り勘トランザクションを作成する。

いよいよ本題の割り勘スマートコントラクトです。

```js

//転送トランザクション
function createTx(address,amount){
  return tx = sym.TransferTransaction.create(
    sym.Deadline.create(epochAdjustment),
    address, 
    [networkCurrency.createRelative(amount)],
    sym.EmptyMessage,
    networkType
  );
}

//BobとCarolが3XYMをAliceに渡し、
payToAliceTx = createTx(alice.address,3);
//Aliceがショップに9XYM渡す。
payToShopTx = createTx(shop.address,9);

aggregateArray = [
  payToAliceTx.toAggregate(bob.publicAccount),
  payToAliceTx.toAggregate(carol.publicAccount),
  payToShopTx.toAggregate(alice.publicAccount)
];
```

必要な情報が少し多いのでまとめます。
Shopのアドレス（支払先に使用）
Aliceのアドレス（支払のまとめ先に使用）
Aliceの公開鍵（署名に使用）
Bobの公開鍵（署名に使用）
Carolの公開鍵（署名に使用）


# トランザクション発行

ネットワークにトランザクションをアナウンスします。
署名が集まるまではノードに保留させておく必要があるので、
ハッシュ値で照合可能なようにハッシュロックトランザクションで
ネットワークに留め置きをお願いしておきます。

```js
aggregateTx = sym.AggregateTransaction.createBonded(
    sym.Deadline.create(epochAdjustment),
    aggregateArray,
    networkType,
    [],
).setMaxFeeForAggregate(200, 2);


signedAggregateTx = alice.sign(aggregateTx, generationHash);

hashLockTx = sym.HashLockTransaction.create(
    sym.Deadline.create(epochAdjustment),
    networkCurrency.createRelative(10),
    sym.UInt64.fromUint(480),
    signedAggregateTx,
    networkType
).setMaxFee(200);

signedLockTx = alice.sign(hashLockTx, generationHash);

listener.open().then(() => {
    transactionService.announceHashLockAggregateBonded(
      signedLockTx,
      signedAggregateTx,
      listener
    ).subscribe(aggTx => console.log(aggTx))
});

//しばらくして以下のメッセージが表示されます。
//> Promise {<pending>}
//> AggregateTransaction {type: 16961, networkType: 152, version: 1, deadline: Deadline, maxFee: UInt64, …}
```

最下行のAggregateTransactionが表示されればボンデッド成功です。

# 連署

ウォレットでBob、Carolのアカウントで履歴を確認します。
ブロック高がパーシャルのアグリゲートボンデッドがあるのでクリックして署名します。
表示されていない場合はFilter右横のリロードをクリックしてください。


![スクリーンショット 2021-12-08 23.58.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/6a896b92-5b8a-3402-e961-894f7ed67b08.png)

内容をよく読んで署名します。送金額にはお気をつけください。

![スクリーンショット 2021-12-09 0.00.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/1b09bd15-5be7-9c9b-ee6e-76e550fe83aa.png)


# 確認

エクスプローラーでもう一度確認してみましょう。


```js
function showTx(hash){
  console.log("https://testnet.symbol.fyi/transactions/" + hash);
}

showTx(signedAggregateTx.hash);

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/764bf533-bf0e-07b5-2d83-620ae03adef8.png)


はい、これで預かったXYMを必ず支払いに使わせるスマートコントラクトが出来ました。
アカウントベースのスマートコントラクトと違い、このスマコンは後に悪用されることもありませんし、数が増えても配列の数と送金の方向を変えるだけで自由自在に変更することができます。
みなさんもお試しください。