ETHのtestnet faucetを作る with firebase cloud functions

#はじめに
FirebaseのCloud Functionsを使ってNode.js(TypeScript)のETH testnet用トークン配布システムを作ります:fire:

#事前準備
まず、何らかの方法でETHのアドレスと秘密鍵を用意してください
あらかじめtestnet(ここではrinkeby)のETHを入れておきましょう

[コードから作成する方法](https://qiita.com/YuasaJunki/items/b56650c7e68af0057703)

#セットアップ

firebaseプロジェクトを[こちら](https://console.firebase.google.com/?hl=ja)から作ってください

firebaseのCLI入れてない方は入れましょう

```
npm install -g firebase-tools
```

```
mkdir eth-faucet
cd eth-faucet
```

```
firebase login
firebase init
```

ここでFunctionsを選択
<img width="587" alt="スクリーンショット 2019-05-06 14.42.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326169/fe9a4bf5-fed4-9205-30d0-fd394ef8c3f6.png">

その後「Select a default Firebase project for this directory:」と言われるので先ほど作ったFirebaseのプロジェクトを選択

※反映されてない場合は「don't setup a default project」を選択し、あとで```firebase use --add [project-id]```とすればOK

<img width="548" alt="スクリーンショット 2019-05-06 14.47.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326169/15d2b062-8aab-1f88-4254-aed0e40c200f.png">

TypeScriptを選択しTSLintも導入してもらいましょう

最後に[Firebase Console](https://console.firebase.google.com/?hl=ja)でプロジェクトの課金設定をBlazeにします

理由は無料版だとfirebase関連以外の外部パッケージが使えないからです

<img width="1320" alt="スクリーンショット 2019-05-06 14.55.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326169/61616e6b-ca55-8a90-b06d-a5f4ef6828a9.png">

<img width="795" alt="スクリーンショット 2019-05-06 14.55.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326169/64f29a2f-c2a4-f556-9e24-40a243cc573e.png">

#作っていく

Express, web3, ethreumjs-txを導入します
ExpressはNode.jsの最小限なフレームワークです
Expressの型定義ファイルもインストールします

```
cd functions
npm install --save express web3 ethereumjs-tx
npm install --save-dev @types/express
```
次に```src/index.ts```を編集します
全体のコードはこちらです

```src/index.ts

import * as functions from 'firebase-functions';
import * as Express from 'express';

const Web3 = require('web3');
const Tx = require('ethereumjs-tx');

const app = Express();

const networks  = {
  main: 'wss://mainnet.infura.io/ws/v3/',
  ropsten: 'wss://ropsten.infura.io/ws/v3/',
  kovan: 'wss://kovan.infura.io/ws/v3/',
  rinkeby: 'wss://rinkeby.infura.io/ws/v3/'
};

// InfuraのAPI-KEY（作る)
const apiKey = 'YOUR-API-KEY';
// テストネット(ここではrinkebyを選択)
const network = networks.rinkeby;

// rinkebyネットワークに接続する処理
const getWeb3 = () => {
  const provider = network + apiKey;
  const web3 = new Web3(provider);
  return web3;
}

app.get('/:address', async(req:Express.Request, res: Express.Response) => {
    try {
        const from = '用意したETHアカウントのアドレス';
        const to = req.params.address;
        const privateKey = '用意したETHアカウントの秘密鍵';
        const amount = 0.01  // faucetとして配布したいETHの量
        const web3 = getWeb3();
    
        const value = web3.utils.toWei(amount.toString(), 'ether');
    
        const gasParams = {
            from: from,
            to: to,
            value: value,
        }
        const gasLimit = await web3.eth.estimateGas(gasParams);
        const gasPrice = await web3.eth.getGasPrice();
        const count = await web3.eth.getTransactionCount(from);
    
        const data = null;
        const id = await web3.eth.net.getId();
    
    
        const params = {
            nonce: web3.utils.numberToHex(count),
            gasPrice: web3.utils.numberToHex(gasPrice),
            gasLimit: web3.utils.numberToHex(gasLimit),
            to: to,
            from: from,
            value: web3.utils.numberToHex(value),
            data: data,
            chainId: id
        }
        //Transaction作成
        const tx = new Tx(params);
        const _privateKey = Buffer.from(privateKey.slice(2), 'hex');
    
        //Transactionに秘密鍵で署名
        tx.sign(_privateKey);
        const rawTx = '0x' + tx.serialize().toString('hex');
        const result = await web3.eth.sendSignedTransaction(rawTx);
        
        //Transactionを送信した結果がtrueならresponseを返す
        if(result.status == true) {
            res.send('送金が完了しました\n')
        }
    }
    catch(error) {
        res.status(500).send(error)
    }
})

exports.app = functions.https.onRequest(app);
```
その後以下を実行

```
firebase deploy --only functions:app
```
そしてcurlでルートにETHの送り先アドレスを込めて叩く

```
curl https://us-central1-<project-id>.cloudfunctions.net/app/<送り先のアドレス>
```
コンソール上で送金が完了しましたと出ればOK

この時、```Error: could not handle the request```のようにtimeout errorが出ることがありますがEtherscanでみるときちんと送金はされています