Gas Station Network でユーザーがGas代を払わずに、トランザクションが実行できるようにする

#はじめに

先月devcon5に参加した中で、OpenZeppeliの発表のなかでGSNなどの発表があったので、戻ってきてから色々と試してみた覚書です。

![unnamed.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/adabe092-192c-49e8-13ff-227c5cad0f56.jpeg)

ユーザーがDappsを使う際に、少量のGasを必要とすることが、DApssが普及するため障壁の1つであることは、すでに知られている通りです。これを解決するために、OpenZeppelinからGas Station Networkを用いて、これらを解決する一つの手段が提供されています。

(Introductionから引用)
https://forum.openzeppelin.com/t/advanced-gsn-gsnrecipientsignature-sol/1414

>
Using GSN allows developers to build (d)apps that don’t require end users to have ETH or even have their own wallet. 
It does this by allowing for a GSN enabled smart contract to pay for its users transactions itself. 
The question then of course is: how does a smart contract determine who is allowed to have their transactions subsidized? 
If there is no criteria, it would be trivial for a user in bad faith to exploit a (d)app and obtain unlimited free transactions.
OpenZeppelin provides two sample strategy contracts which can be used to help (d)app developers decide who their recipient contacts are willing to pay for. ...

GSNを使うことで、開発者は"エンドユーザーがWalletにEthereumを必要としないDApps"を構築することができます。
これは、GSN対応のスマートコントラクトが、ユーザートランザクション自体に支払いを行うことを可能にすることで、実現しています。
「スマートコントラクトは、誰がGasの補助を受けることができるのかをどのように決めるのか？」という疑問は至極、当然です。
基準がない場合、悪意のあるユーザーがDAppsを悪用して無制限の無料トランザクションを取得するのは簡単なことです。
OpenZeppelinは、（d）アプリ開発者が受信者の連絡先の支払いを決定するのに役立つ2つのサンプル戦略契約を提供します。

(参考)
https://forum.openzeppelin.com/t/using-gas-station-network-starter-kit-on-a-public-testnet/1429

https://github.com/OpenZeppelin/starter-kit-gsn

1–800-Ethereum: Gas Stations Network for Toll Free Transactions
https://medium.com/tabookey/1-800-ethereum-gas-stations-network-for-toll-free-transactions-4bbfc03a0a56

https://medium.com/coinmonks/how-dapp-users-can-interact-with-your-smart-contracts-with-zero-eth-d0b664b355ce

#実行サンプル
https://gsn-chat-app.openzeppelin.com/

<img width="454" alt="スクリーンショット 2019-11-01 16.20.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/344031bc-12b0-bfd2-7195-5b32803e0818.png">


#EIP-1613

GSNの基本的なデザインはTabooKeyによって考案され、EIP-1613に提案されています。

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1613.md

![alt](https://camo.githubusercontent.com/a134d300b5bf6829fa3667ea21d76046f082b1e6/687474703a2f2f6269742e6c792f32575a714d3233)


#使ってみる

##1.Development環境で使ってみる

```
$ npm install -g ganache-cli
$ ganache-cli --deterministic

//プロジェクトのディレクトリを作成する
$ mkdir myproject
$ cd myproject

//GasStationNetworkのkitを展開する
$ npx @openzeppelin/cli@next unpack gsn
$ npx openzeppelin init

//clientアプリケーションを展開する
$ cd client
$ npm run start

//作成する
$ npx openzeppelin create
Nothing to compile, all contracts are up to date.

//質問：コントラクトの名前 (Counterを選択)
? Pick a contract to instantiate Counter
//質問：使用するネットワーク(development を選択)
? Pick a network development
All contracts are up to date
? Do you want to call a function on the instance after creating it? Yes
? Select which function * initialize(num: uint256)
```

##2.Testnet環境で使ってみる

###App.jsを書き換える

元は、127.0.0.1:8545に接続されていたものをinfuraに接続するコードを有効にして、127.0.0.1の方のcontextをコメントアウトする。下記のような感じでok。

```client/src/App.js
  // get GSN web3
   const context = useWeb3Network(`wss://rinkeby.infura.io/ws/v3/${infuraToken}`, {
     pollInterval: 15 * 1000,
     gsn: {
       signKey,
     },
   });
/*
  const context = useWeb3Network('http://127.0.0.1:8545', {
    gsn: {
      dev: true,
      signKey,
    },
  });
*/
```

###MNEMONICSを生成して、.envに記述する

```
$ npx mnemonics
attack crane rabbit share proof truck glad next female win regular waste
```

###.envに下記を設定する

```
$ touch .env
MNEMONIC="attack crane rabbit share proof truck glad next female win regular waste"
```

###アカウントを取得する

```
$ npx openzeppelin accounts
? Pick a network (Use arrow keys)
❯ rinkeby 

Accounts for rinkeby:
Default: 0x6303b56db2d46fb34EE996E8f3Db58793e7d97dE
All:
- 0: 0x6303b56db2d46fb34EE996E8f3Db58793e7d97dE
```


###このアドレス(Accounts for rinkeby:)にいくらかEthereumを送っておく

###Contractをdeployする

```
$ npx openzeppelin create
? Pick a contract to instantiate Counter
? Pick a network rinkeby
? Do you want to call a function on the instance after creating it? Yes
? Select which function * initialize()
```

(下記のように怒られたら、GASが足りてない)
✖ Creating instance for contract at 0x706Ffbc6470cBeB7be153750a119140D5E1f9970 and calling 'initialize' with no arguments
insufficient funds for gas * price + value


###インスタンスが生成される

```
✓ Instance created at 0x0385451CfBd7DaDf9506d018E57d2fD4b9CA8604
0x0385451CfBd7DaDf9506d018E57d2fD4b9CA8604
```

###下記リンクから、作成されたインスタンスのアドレスを入力する
https://gsn.openzeppelin.com/recipients

###Walletと接続するを選択する
<img width="1073" alt="スクリーンショット 2019-11-01 11.21.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/54514d66-bc64-4245-4340-03d8e3526c6a.png">


###デポジットを送金しておく
<img width="1149" alt="スクリーンショット 2019-11-01 11.23.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/fff2aee5-704e-fe13-a9a6-7385c9a97f86.png">


###CLientを起動

```
cd client
npm run start
```

###左側がClientで、0Ethereum。右側が作成したCounterContractで、先ほど送った0.5Ethがある状態。IncreaseCounterを押下すると...
<img width="920" alt="スクリーンショット 2019-11-01 11.27.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/a50a7d70-2652-62d1-27ac-92575b4cce49.png">

###CounterValueが1になって、IncreaseCounterのトランザクションが成功。
<img width="902" alt="スクリーンショット 2019-11-01 11.28.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/064d835e-e9c7-d3db-193c-d121433f0e38.png">

https://rinkeby.etherscan.io/tx/0x720a462d75d922ade3f27812c89bc1b5790ddfc4b58186791dcbf9aefc56a1e9

##remixから実行

RemixやWalletを使うと、Gas=0のトランザクションを作ることはできないため、GSN経由にならずに普通にGasを支払って、コントラクトを実行するような流れになります。

<img width="607" alt="スクリーンショット 2019-11-01 14.58.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/b32be86e-3319-aedf-ff8b-658910ef176a.png">

```
1.環境設定
Metamask->Rinkeby
Rimix->Run->Environment:injectedWeb3 Account:Metamaskのaccount

2.Console
cd workspaces/
//実行したいCounter.solの位置を確認
less contracts/Counter.sol 
//remixdを実行
remixd -s ./ --remix-ide https://remix.ethereum.org

3.Remixでlocalhostに接続
->Compile->localhost/contracts/Counter.sol

4.load
Remix->Run->Counter->AtAddress(上記でdeployしたアドレス)

5.試す
getCounter() -> 1
```

##Reactから実行





#おしまい
そういえば、OpenZeppelinって、自分の中では、なんとなくSmartContractの雛形みたいなイメージだったのですが、先日devcon5 OsakaのOpenZeppelinのセッションを聞いて、かなりSDK寄りになったというか、開発者が欲しいサービスがどんどんアドオンされているイメージを受けました。GSN以外にも、使いやすそうなものが展開されているようなので、リサーチしてみたいなと思っています。