27
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FOLIOAdvent Calendar 2017

Day 24

IOTA ~ポストスマートフォン時代のフィンテック~

Last updated at Posted at 2017-12-23

こんにちは。株式会社FOLIOでCTOをしている椎野と申します。今年のTechCrunch2017 CTO Nightでは第2創業期のCTOとはというタイトルで話もさせていただきました。

この記事はFOLIO Advent Calendar24日目の記事です。昨日は@kndoshnの**SwiftエンジニアがReact Nativeでアプリを作った話**でした。

今年2017年は仮想通貨フィーバーが巻き起こった年として歴史に刻まれるのではないかと思います。Bitcoinは年初に1btcが10万円ほどだったのが2017年12月には200万円前後を推移していて一年で20倍の値がつくなどものすごいボラティリティで成長しています。そしてそれら仮想通貨の仕組みのコアでもあるBlockchainに関する取り組み事例やサービスも脚光を浴びています。

私達FOLIOも証券という切り口から新しいフィンテック市場を創出したいということもあり、仮想通貨やBlockchainの仕組みを利用した新しいサービスを日々考えています。

本日は、次世代のBlockchainとも言われているTangleとそれを実装したIOTA(アイオータ)という仮想通貨に関して書きたいと思います。まだ細部を全て理解できていない部分もありますが、このような仮想通貨も世の中あるのだなと思ってもらえれば幸いです。私は本執筆を通じてIOTAが大好きになりました。

#IOTAとは
そもそもIOTAとは何なのか。なぜBlockchainで実装されていないのか。IOTAはIoTで利用されることを想定とした仮想通貨で、あらゆるデバイス間で通貨の受け渡しが可能になるということになります。既存のBlockchainを利用した仮想通貨ではできないのかというと、以下の3つの点において困難だと言われています。

  1. スケーラビリティが弱い
  2. トランザクション手数料
  3. ハイスペックなハードウェアが必要

例えばBitcoin決済における合意形成(承認)処理はトランザクションが増えるほど時間がかかるスケーラビリティ問題が明白だったり、合意形成処理を行うハードウェアリソースに対するコストの上昇に伴うトランザクションフィーの上昇など、あらゆるデバイス上で高速に仮想通貨を扱うにはこのような問題を乗り越えなければなりません。

IOTAの特徴は以下になります。

  1. スケーラビリティが高い
  2. マイクロペイメントに対応
  3. トランザクション手数料無料
    IOTAの特徴は言い換えると後述のTangleの特徴と言えます。

IOTAは2015年12月に公開され、まだ新しい仮想通貨ということもあり、現在取引できるのはbitfinexbinanceおよびcoinone のみで、国内の取引所ではまだ扱われていません。日本から購入する際はBitcoinやEthereumなどをそれらの取引所に送金する必要があります。(購入の仕方は後述)

#Tangleとは
IOTAの根幹をなすのがTangleという仕組みです。TangleはDAGというアルゴリズムで実現しています。DAGとはDirected Acyclic Graphの略称で翻訳すると有向非循環グラフという意味になります。以下の図のようにどこから出発しても、同じ場所に戻ることなく、一方向に伝搬してゆく仕組みになっています。
dag_graph.png
IOTA Deepdiveにある図より」引用

Tangleもblockchain同様に新しいトランザクション(Tangleではチップと呼びます)を生成する際に合意形成(承認)が行われますが、Bitcoinのblockchainのようにブロックを生成してトランザクションを追記するマイニング処理がTangleでは必要ありません。Tangleでの承認処理は以下の図のように行われます。
スクリーンショット 2017-12-23 9.30.06.png
新規トランザクションを生成する際に過去の2つのトランザクションを承認する必要があるのですが、ルート(チップ)トランザクションが全て承認した状態のものが最終の承認となります。(赤線で承認がされたもの)

このようにTangleでは特定のマイナーの存在は必要なく、取引を行った人(デバイス)が過去に行われた取引を承認する仕組みとなっているので、トランザクションが膨大になったとしても、取引者が承認者なのでトランザクションが増えても承認者も増えるので、そのため承認の負荷が分散されます。マイニングがないので取引手数料も必要がないことになります。Bitcoin(blockchain)のように取引をブロック化せず、取引情報単位で高速な承認処理が可能なので、従来のblockchainでは困難なIoTデバイス上でのマイクロペイメントが可能です。

#Tangleにおけるトランザクションとその取引の承認
Tangleで取引を承認する上で、どのようなプロセスを経ているのか追ってゆきたいと思います。大まかな流れは以下のようになっています。
スクリーンショット 2017-12-23 9.49.23.png
※詳しくはIOTA Deepdiveの情報を参考にしています。

送金元アドレスの生成、署名、トランザクションの生成を行います。
引用: Make an address 〜 Validate the signature
##アドレス生成
図の流れの通り、長さ81文字以下の大文字アルファベットのA-Zと数字の9で表されるシードを元にしてアドレスを生成します。
スクリーンショット 2017-12-23 10.24.06.png

Tangleではトライト(Tryte)という単位で値を表すことが多いです。これは3進数のトリット(trit:2進数のbitと同じ粒度表現)をベースとしていて、0、+、-でA-Z、9を表現します。今のところシードは81文字以内で3進数単位(A-Z, 9)であれば何でも良いので脆弱なシードでもアドレスが生成されてしまうのが難点ですが、現状はしっかり81文字でかつランダムなシードを設定するのが良いです。

PBUFXXVPNUYINACQNQONYSZUJEZRGXCKTJAZUFOOKABPSTFOXXYWEIOPNDIVIQOZPVQMNWGWXPTMT9IYP

フロー1:シードからPrivate Keyを生成します。Keyはシード、インデックス、長さを変数として生成されるハッシュ値になります。

フロー2:Private Keyをセグメントに分けて、それぞれのセグメントを27回ハッシュ関数を通して、Public Keyを生成します。

フロー3:Public key全体をさらにハッシュ関数にかけてDigestを生成し、Digestをさらに2回ハッシュ関数にかけるとアドレスが生成されます。

##署名
アドレス生成と同様に、長さ81文字以内のシードをセグメント化して、署名するデータに対してN回ハッシュ関数をかけます。このN回の回数は署名するデータの1トライト(A-Z、9のいずれか)が何かで決まります。
スクリーンショット 2017-12-23 14.50.15.png
Nの求め方
'9':27-0=27回
'A':27-1=26回
'C':27-3=24回
(弱点としてZの場合だと1回しかハッシュ関数を通さないということがあげられる)
( 'M'だとハッシュが通らないというバグがありましたがすでに修正されているようです。 )
ハッシュ化した値を連結したものが署名になります。

##トランザクションの生成〜送金
トランザクションの生成に移りますが、ややこしいのですが実は取引(送金)プロセスにおいて、1トランザクションでは完結せず、4つのトランザクションが必要で、アウトプットとインプットのトランザクションのBudleで構成されています。Bundle自身のハッシュによってトランザクションが特定されます。
スクリーンショット 2017-12-23 14.34.25.png
上の図の通り、署名はトランザクション[1]と[2] に半分づつ分けて格納されます。この2つの署名を検証することでバンドルに含まれる取引トランザクションの承認を行うことになります。

##署名の検証:承認
最後に取引(送金)時に生成されたトランザクションに格納された署名を検証します。
こちらもアドレス生成と同様に署名をセグメント化して、セグメントごとにM回ハッシュ化します。
スクリーンショット 2017-12-23 15.04.39.png
このM回の回数は署名されたデータの1トライト(A-Z、9のいずれか)が何かで決まります。
Mの求め方
'9':0回
'A':1回
'C':3回
ここで生成されたハッシュ値を連結して、全体をハッシュ化しdigestを生成し、そのdigestをさらに2回ハッシュ化して得られた値がアドレスと一緒であれば検証OK=承認となります。

このトランザクションの弱点としては、同一のシードから生成されるキーは同一になるので、キーが一回わかってしまうとトランザクションを乗っ取ることができてしまうことになります。なのでキー生成にはインデックスという引数を利用して、このインデックスを+1してゆくことで同一シードからでも異なるキーを生成する工夫がなされています。

iota.multisig.getKey(seed, index, security);

このTangleのトランザクション認証方式は次世代の量子コンピューティングを見据えてのものとも言えます。量子コンピューターの登場でBlockchainを使った仮想通貨が駆逐されるという記事が一時期出ていましたが、暗号化キーを量子コンピューターの驚異の計算速度で解読することが早晩可能にもなりそうとのことで警報が鳴らされています。(あとはそもそもBlockchainの新しいブロックの生成において難易度の高い計算問題を争って解くということが前提とされていて、量子コンピューターがマイニングを始めた途端、勝てるマイナー(承認者)がいなくなり、結局量子コンピューターの中央集権的なレガシーなサーバー、クライアント型になってしまう為に、元々の分散台帳の意義がなくなるとも言えます。)Tangleは前の2つのトランザクションを承認するのみで、元々計算力をあまり必要としない仕組みなので量子コンピューターの計算処理速度はほぼ意味をなさないということらしいです。

#IOTAの購入
まず、IOTAコインは国内の取引所からは購入できないので、2017年12月の段階では唯一の取引所であるbitfinexに取引可能な仮想通貨を送金して、IOTAコインをその仮想通貨で購入します。

##ログインから初期設定まで
スクリーンショット 2017-12-23 15.22.59.png
右上のLOG INボタンを押すと上記のようなログインダイアログが表示されます。まだbtfinexにアカウント登録していない方はSIGN UPからアカウント登録をしてください。

スクリーンショット 2017-12-23 15.26.32.png
ログインできましたら上記のチャート画面が表示されます。取引を始めるにあたり、セキュリティを高めるために2要素認証を最初に設定しておきます。

スクリーンショット 2017-12-23 15.30.24.png
画面の一番右上のユーザーアイコンをクリックします。メニューがロールダウンされますので、Securityメニューをクリックします。

スクリーンショット 2017-12-23 15.29.03.png
セキュリティ設定画面が表示されますので、上から2つ目のTwo-Factor Authenticationのエリアを開いてください。現在は表示されている3つの方法で2要素認証の設定ができます。今回はGoogle Authenticatorを利用したいと思います。SETUPボタンを押すと認証コードを求められますので、Google Authenticatorで表示される認証コードをこちらに記載することで2要素認証の設定が完了します。セキュリティを高めたところで、続いてIOTAコインの購入を行ってみたいと思います。

##入金からIOTAコイン購入まで

スクリーンショット 2017-12-23 15.41.31.png
画面右上(画面)のDepositをクリックして、Ethereumを選択します。

スクリーンショット 2017-12-23 15.52.59.png
Exchange WalletのClick to generate addressをクリックすると送金用のアドレスが表示されます。
コピーして、自分のアカウントがある日本の取引所からこのアドレスを利用して送金をします。今回はEthereumでIOTAコインを購入するため、国内で利用している取引所からこのbitfinexの自分のEthereum用のウォレットアドレスあてにEthereumを送金します。

スクリーンショット 2017-12-22 18.05.38.png
デポジットに送金したEthereum(0.1ETH)があることをこちらで確認します。確認が取れたらいよいよIOTAコインとのトレード(取引)を実施します。

スクリーンショット 2017-12-22 18.08.16.png
メイン画面左上のTradingをポイントするとメニューが表示されます。Iota→IOTA/ETHを選択します。

スクリーンショット 2017-12-22 18.09.16
メイン画面右側のCHARTに過去12時間分のエキスチェンジプライスのチャートが表示されています。画面左のORDER FORMでEthereumとIOTAを交換レートにしたがって交換(購入)します。オプションは様々ありますが、今回は一番オーソドックスなLimit Order(いわゆる株式売買における指値注文)でIOTAを購入してみます。

スクリーンショット 2017-12-23 16.05.39
上の画面はORDER FORMの画面になります。AMOUNT MIに欲しいIOTA額を入力します。
PRICE ETHにIOTAとの交換レートが表じされています。今回は15MI(M単位で入力)を0.08ETHで指値注文します。最後にExchange Buyのボタンを押します。

スクリーンショット 2017-12-23 16.14.08
注文が出されるとORDERSに表示されます。指値で購入成立するまでの時間は相場の変動の仕方によってまちまちですが、しばらく待っていると購入されます。

スクリーンショット 2017-12-23 16.17.59
購入が成立したら、画面左のBALANCESに購入したIOTAコインの額が表示されます。

#IOTAウォレットの利用
IOTA用のライトウォレットがgithubからダウンロードできます。
https://github.com/iotaledger/wallet/releases/
今回はMac用のものをダウンロードしてインストールしてみます。

##ウォレットアプリの起動
スクリーンショット 2017-12-23 16.30.58
インストールが完了したらノードの選択を最初に行います。フルノード(全てのトランザクション情報を同期)とライトノード(別のフルノードホストから情報をもらって動作)がありますが、マイニングを行わないIOTAでは特別フルノードになる必要はなさそうなのでライトノードを選択します。

スクリーンショット 2017-12-23 16.37.22 ホストの選択は、先ほど利用したbitfinexを選択します。(原則どこでも構いません。)

先ほどのTangleでの説明の通りシードからアドレスを生成するために、81文字以内のトライト(A-Z,9)をシードとして設定します。シード生成の際、パスワード生成をするウェブサイトを利用する場合も必ずオフラインで生成するようにしましょう。不安な方は以下のコマンドで生成できます。

cat /dev/urandom | LC_ALL=C tr -dc 'A-Z9' | fold -w 81 | head -n 1
スクリーンショット 2017-12-23 16.44.05 生成したシードをSeed入力欄に入力します。IOTAウォレットではシードの入力のみでログインができます。 スクリーンショット 2017-12-23 16.45.48 ログインが成功すると上の画面が表示されます。現在はこのウォレットの残高は0です。

##ウォレットに送金してみる
スクリーンショット 2017-12-23 16.48.24
受取欄をクリックするとアドレスが生成されます。このアドレスは先ほどログインの際に使用したシードを元に作られています。タングルにアタッチをクリックすると生成されたアドレスがTangleにアタッチされます。

スクリーンショット 2017-12-23 17.01.57 先ほど購入したIOTAをこのウォレットに送金してみます。シードから生成されたアドレスが表示されているので、bitfinexのIOTAデポジットからウォレットのアドレスを指定して送金します。

続いてbitfinexから先ほど購入したIOTAコインをこのウォレットに送金します。
スクリーンショット 2017-12-23 17.14.13
画面右上のWithdrawからIotaを選択すると上の画面が表示されます。ウォレットのアドレスをAddressとして入れ、Send Amountに送金額、fromはbitfinexでIOTAコインを保持しているウォレットを選択します。Request Withdrawボタンを押していざ送金!となるはずだったのですが、最低送金額が設定されていて、その額を保有しておらず、送金できませんでした。。さらに手数料もかかるらしく、IOTAの特徴でもあるNO FEEはどこにすっとんでしまったのだろうかと。。ここからの検証は、状況をみて再開したいと思います。

#IOTAのテクノロジーを体験してみる
IOTAファンデーションのgithubからIOTA向けの様々なオープンソースのコードがリリースされています。
スクリーンショット 2017-12-23 17.29.23
「iri」はいわゆるノードのホストとなるJavaアプリケーションになります。IOTAのTangleネットワークの運用に参加したいユーザーは、こちらを使ってみることができます。また、先ほど同じgithubからウォレットアプリをダウンロードして利用してみましたが、このgithubには「iota.lib」というiriへの接続APIをラッピングしたライブラリがJS版、Python版、Java版、GO版、C#版などで提供されているので、こちらを使って自作のウォレットなども作ることができます。

##iriを起動してみる
IOTAということで、ここはやはりIoTにこだわるべきかと思いまして、Raspberry PI3にiriをインストールしようと思いましたが、時間の都合上Macで試しました。もちろんWindows、Linuxでもインストールできます。
ちなみにRaspberry PI3にiriをインストールする方法はこちらにあります。

iriの起動は以下の2パターンあります。

  1. Javaで起動する
  2. Dockerコンテナとして起動する
    基本的にどちらもで構わないのですが、コンテナからの起動の方が、環境依存度が低いので比較的楽だと思います。
# ソースからコンパイルする場合
# コンパイルにはmavenとJava8以上がインストールされている必要があります

# githubからiriをcloneする
git clone https://github.com/iotaledger/iri
cd iri
# mavenでソースをコンパイルする
mvn clean compile
mvn package

# iriを起動: 慣例的にポート14265でリッスンする状態で起動する
java -jar iri.jar  -p 14265
# dockerからインストール起動する場合
# dockerがインストール、起動している必要があります

# 以下は例となります。
docker run -d --net=host --name iota-node -p 14265:14265 -p 14777:14777/udp -p 15777:15777 -v iota.ini:/iri/iota.ini iotaledger/iri:latest

起動するとログのモニターで以下のような通信がキャプチャできます。

[main] INFO  com.iota.iri.IRI - Remote access enabled. Binding API socket to listen any interface.
[main] INFO  com.iota.iri.IRI - Welcome to IRI 1.4.1.2
[main] INFO  c.i.i.s.r.RocksDBPersistenceProvider - Initializing Database Backend... 
[main] INFO  c.i.i.s.r.RocksDBPersistenceProvider - RocksDB persistence provider initialized.
[main] INFO  com.iota.iri.network.UDPReceiver - UDP replicator is accepting connections on udp port 14777
[main] INFO  c.i.i.network.replicator.Replicator - Started ReplicatorSourcePool
[UDP receiving thread] INFO  com.iota.iri.network.UDPReceiver - Spawning Receiver Thread
[UDP receiving thread] INFO  com.iota.iri.network.UDPReceiver - Receiver thread processed/dropped ratio: 0/0
[pool-2-thread-1] INFO  com.iota.iri.network.Node - Spawning Broadcaster Thread
[Thread-2] INFO  c.i.i.n.r.ReplicatorSourcePool - TCP replicator is accepting connections on tcp port 15777
[pool-2-thread-2] INFO  com.iota.iri.network.Node - Spawning Tips Requester Thread
[pool-2-thread-4] INFO  com.iota.iri.network.Node - Spawning Process Received Data Thread
[pool-2-thread-3] INFO  com.iota.iri.network.Node - Spawning Neighbor DNS Refresher Thread
[pool-2-thread-3] INFO  com.iota.iri.network.Node - Checking Neighbors' Ip...
[pool-2-thread-2] INFO  com.iota.iri.network.Node - toProcess = 0 , toBroadcast = 0 , toRequest = 0 , toReply = 0 / totalTransactions = 0
[pool-2-thread-5] INFO  com.iota.iri.network.Node - Spawning Reply To Request Thread
[main] INFO  com.iota.iri.IRI - IOTA Node initialised correctly.

IOTAノードの初期化までをウォッチしてみました。データを同期するneighbor(他のiriホスト)を指定していないのでプロセス、リクエスト、ブロードキャストなどが0の値になっています。起動する際に、通常はpublic(IOTA本番ネットワーク)あるいはtestnet(検証用)への接続が選択できますが、検証用途ではtestnetに接続して、neighborを指定してサービスを開始します。

[IRI]
PORT = 14700
UDP_RECEIVER_PORT = 14700
NEIGHBORS = udp://my.favorite.com:15600
IXI_DIR = ixi
HEADLESS = true
DEBUG = true
DB_PATH = db

これでMacをiriノードホストデバイスとすることができました。

##ウォレットを自作してみる
IOTAファンデーションからライブラリがリリースされているので、今回はJSのライブラリであるiota.lib.jsを利用して、ノードへのアクセス、残高のチェックなどを行ってみたいと思います。
スクリーンショット 2017-12-23 17.29.23

グラフィカルに作っても良いのですが、今回は動作チェックのみなので、Node JSでコマンドベースでそれぞれ動作の確認をします。まずnpmコマンドからiota.lib.jsをインストールします。(以下は自作ウォレット用の作業ディレクトリをベースのロケーションとします)

npm install iota.lib.js

self-wallet.jsというファイルを作成して、そこに検証用walletのコードを実装してゆきます。

self-wallet.js
const IOTA = require('iota.lib.js')
  
//iriホストへ接続
const iota = new IOTA({
    host: 'https://nodes.iota.cafe',
    port: 443
})

//ノードの情報を取得
iota.api.getNodeInfo((error, nodeInfo) => {
    if (error) {
        console.error('error', error)
    } else {
        console.log('success', nodeInfo)
    }
})

コマンドラインでこのself-wallet.jsを動かしてみます。

node self-wallet.js

今回は以下のような結果が表示されました。

result
# ノードの情報がJSON形式で表示
test
{ appName: 'IRI',
  appVersion: '1.4.1.4',
  jreAvailableProcessors: 4,
  jreFreeMemory: 1805556888,
  jreVersion: '1.8.0_91',
  jreMaxMemory: 4294967296,
  jreTotalMemory: 4294967296,
  latestMilestone: 'OQHGTJWDAHJAJUDKGIHTCMMCGIHXZZCWJTXLOPLGYNRMWKOTDRRAHNIQGY9KNOKXJWBXHIQJYSCRA9999',
  latestMilestoneIndex: 309902,
  latestSolidSubtangleMilestone: 'OQHGTJWDAHJAJUDKGIHTCMMCGIHXZZCWJTXLOPLGYNRMWKOTDRRAHNIQGY9KNOKXJWBXHIQJYSCRA9999',
  latestSolidSubtangleMilestoneIndex: 309902,
  neighbors: 5,
  packetsQueueSize: 0,
  time: 1514020443223,
  tips: 5431,
  transactionsToRequest: 162,
  duration: 0 }

次にライトウォレットと同様にシードからアドレスを生成するまでを試してみたいと思います。フローに関しては、前述のトランザクションの流れと同じになります。

self-wallet.js
//続いてシードからアドレスを生成
const seed = '9R9GW9GCUUCUAIDLUWUNUHCADQHTIREAGZQTUYMSJZATEQOOSK9HWBQFOTOORTPSUUSOOTWFDWWRULSSM'
iota.api.getNewAddress(seed, (error, address) => {
   if (error) {
       console.error('error', error)
   } else {
       console.log('success: ' + address)
   }
})

以下のアドレスが生成されました。

result
YRNBLEZJPCVMMSXYTTDKGWEKNDCFNUWKBKQBFVVFUOSNZPKRPMYSZGGKOXIOUUTZEOTHNMIETENQILHOB

このあとはsendTransfer関数をコールして送金する流れになるのですが、手元にIOTAコインがないので、この後の検証はまた別の機会に行いたいと思います。

今回はiriの起動とクライアントがどのように動いているのかを確認する非常にライトな検証のみにフォーカスしました。個人的にはpublicのネットワークでiriを動かしてみて、TangleがどのようにIOTAネットワークを保全しているのか確認できたらと思っています。

#IOTA、Tangleの今後の可能性
もともとIoTプラットフォームサービスに以前携わっていたこともあり、IoT x FinTechというジャンルにはすごく興味があったのですが、IoTと聞くとなにやらハードウェアゴリゴリというイメージがどうしても出てきてしまいます。どちらかというとこれまで繋がらなかったところに繋げられるようになるIoTと仮想通貨やそれを支えるBlockchainやTangleのような分散型ネットワークを掛け合わせることで、これまでできなかったことの多くが実現できるのではとサービスの観点からIOTAに興味を持ち始めました。

Blockchainの弱点でもあったスケーラビリティや高いトランザクションフィーなどに関する課題を一気に解決できそうなIOTA、Tangleはnext generation blockchainの呼び名に劣らない可能性を秘めているのではないかと思っています。直近(2017年2月)のガートナー社の調査によると2020年までにインターネット接続デバイス数は204億台(2017年時点では84億台)になると予想しています。IoTのようなデバイズが相互に自律的に動作を行うために、毎回クラウドやローカルのコントローラーサーバー(エッジコンピューティングなど)を介してやりとりが行われるのは、そのコントローラーがボトルネックにもなりうるし、それを介するために非効率な経路でデバイス間のやりとりが発生してしまう課題もあるかと思います。爆発的に増えゆくIoTデバイスが自律的に動作する上でも、状態データを効率的に共有できる可能性が高いTangleはまさに今後のIoTが抱える課題に対しての有用なソリューションの一つと考えられます。そしてこの仕組みを使ってマイクロペイメントが可能なIOTAコインによる新しい決済サービスの誕生にも大きな期待が出てくるものと思われます。

現在のIOTA x TangleはIOTAコインを流通させるのみのプラットフォームですが、プロダクトのロードマップとして今後Privateネットワークにも対応できるようにするという構想もあるようです。この仕組みをコインだけではなく、あらゆるデータが乗せられるよう柔軟性を持った拡張がされていくことを願っています。

明日は、アドベントカレンダー最終日となりますが、弊社のHead of Engineeringの伊藤(@itohiro73)の「株式会社FOLIOの次世代証券システムをひもとく」で締めくくります。

FOLIOでは新しい金融マーケットを一緒に作ってゆく仲間を募集しています!
https://www.wantedly.com/companies/folio/projects

27
20
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?