Slack で reaction をつけてもらった人に ERC20 準拠の Token を付与します。
基本的には この repository のコンセプトをそのまま nodejs で実装しなおした形です。
ユーザーは slack で自分の発言に reaction がつくと、前もって登録しておいた自分の Ethereum アカウントに ERC20 準拠の Token をもらえます。
Slack チーム内のコミュニケーションを促す目的での導入を想定しています。
全体の構成は以下のような形です。
ユーザーの登録は Slack のスラッシュコマンド経由で行います。
/register 0x0000000000000000000000000000000000000000
の形で自分のアドレスを登録できます。登録されたアドレスと slack の userid は mongodb に保存されます。
Reaction は Slack の Event API 経由で受け取ります。
Reaction が発生したら、ユーザーの slack userid から上記で登録した ethereum address を取得します。
また、reaction の種類が予め登録されたものかどうかをチェックし、token を送信するための job を作って queue に入れます。
Queue に入った job は 1 つずつ順番に取り出され、token の付与を行う Tx を作って ethereum に投げます。
マスターアカウントの nonce と Tx の nonce があっていないと処理が失敗するため、job queue を利用して処理を 1 つずつに限定しています。
Queue の処理では、以下の流れで処理を行い、Tx 終了前に次の処理が走るのを(出来るだけ)防ぎます。
// Kue から呼ばれる関数
queue.process('transfer', async (job, ctx, done) => {
let tx = null;
// Tx を作って、ether の network に投げ込む
await Ethereum.sendToken('0x' + job.data.to, async (hash) => {
// Tx は直ぐに作られるので、この Callback は直ぐに呼ばれる
// https://web3js.readthedocs.io/en/1.0/web3-eth.html#sendtransaction
tx = hash;
await reactionService.findByIdAndUpdateTx(job.data.reactionId, hash);
});
// sendToken の結果を await を使って待っているので、ここに来るときには tx の receipt は存在しているハズ
// ただし、50 block 以内に mining が完了しない場合は Tx が失敗していなくても receipt がまだ無い場合がある
let waitCount = 0;
let result = null;
// receipt を取得できるか、120 秒経過するまで待つ
while (result == null && waitCount <= 120) {
if (tx != null) {
// Tx から receipt を取得する
result = await Ethereum.getReceipt(tx);
}
waitCount += 1;
sleep.sleep(1);
}
// Timeout (120 秒) せずに結果が取得できれば保存
if (result != null) {
reactionService.updateStatusToComplete(job.data.reactionId,
result.blockHash,
result.blockNumber,
result.cumulativeGasUsed,
result.gasUsed);
}
// これにより、次の job が dequeue される
done();
});
ERC20 トークンを発行したマスターアカウントの秘密鍵を nodejs のサーバーに環境変数として保管し、Token 付与はそのアカウントから行います。
マスターアカウントが Token 付与のガス代を支払うため、マスターアカウントには Ethereum をディポジットしておく必要があります。
$ sh script/prepare.sh
$ cp ./app/.env.example ./app/.env
内容は以下を参考に自分の環境に合わせて埋めてください
MONGO_DATABASE=slack-token
SALT=
ETHERESCAN_URL=https://<main 以外の場合はネットワーク名>.etherscan.io/
INFURA_ENDPOINT=https://rinkeby.infura.io/<Access Token>
SERVER_ACCOUNT_ADDRESS=<Ether account address>
SERVER_ACCOUNT_PRIVATE_KEY=<Ether account private key>
CONTRACT_ADDRESS=<Deploy 済みの ERC20 token address>
SLACK_CLIENT_ID=
SLACK_CLIENT_SECRET=
SLACK_VERIFICATION_TOKEN=
SLACK_TOKEN=<起動時に emoji.list を叩いてカスタム絵文字を取得するために使う>
# カンマ区切りで記載。指定しなければ全てに反応する
EMOJI=slightly_smiling_face,bug
# カンマ区切りで Slack のチャンネルIDを記載。指定しなければ全てに対応する
CHANNEL=
ここで指定した SERVER_ACCOUNT_ADDRESS
アカウントの保持する ERC20 をユーザーに配布します
app/contract/contracts/CustomToken.sol
の変数を修正してください
string public name = "Token の名前";
string public symbol = "TICKER";
uint8 public decimals = 18; // Token の最小単位
uint256 initialSupply = 10000e18; // 初期発行量 (1 = 最小単位なのでこの場合 10000 token の発行になる)
以下のコマンドでコントラクトを compile できます
$ sh script/contract/compile.sh
compile されたコントラクトは contract/build
フォルダ以下に作られます。
ganache-cli
を利用して、実際に ethereum の network にはコントラクトを deploy せずにテストを行います
$ sh script/contract/test.sh
環境ファイルで設定した INFURA の向先によって、どのネットワークに deploy されるかが決まります
$ sh script/contract/deploy.sh
Deploy されたら、環境ファイルにコントラクトアドレスを記載してください。
// ローカル環境
$ sh script/dev/build.sh
// 本番環境 では ssl を使う関係上、下記コマンドを利用
$ sh script/stg/build.sh
// ローカル環境
$ sh script/dev/start.sh
// 本番環境 では ssl を使う関係上、下記コマンドを利用
$ sh script/stg/start.sh
Slack App を配布するためには https 化する必要があるらしいので、(参考)
Let's encrypt などで取得した fullchain.pem
と privkey.pem
を この repository の root に置いてください (Symbolic link でも問題ありません)
サーバーを起動する際には docker-compose-ssl.yml
を使います。ssl 用の証明書は実行時に volume mount されます。
// 下記コマンドで ssl 設定をしてある nginx image を build します
$ sudo sh script/stg/build.sh
// 下記コマンドで証明書を上記コンテナにマウントしつつ、docker-compose でサービスを起動します
$ sudo sh script/stg/start.sh
Slack token を利用するためには以下のスコープの設定が必要です。
- Slash command Scope
- Reactions Scope
- Emoji Scope
Slack の slash command は /api/register/
に来るように設定してください
Event は reaction_added を購読してください
Slack の管理画面メニューの Event Sbuscriptions
を選択して、Event 送信先の Request URL を指定します。
Add Workspace Event
ボタンから reaction_added
を追加してください。
カスタム絵文字対応を行うために emoji.list API を起動時に呼びます。
API を呼ぶための Token を管理画面から取得して、.env
の SLACK_TOKEN
に指定してください。