Staking の実装と不動産分野での活用
Proof of Stake (PoS) という言葉は聞いたことがあることが多いかもしれません。ステーキングとはトークンをネットワークに預けることで報酬を得ることです。 今回は以前の記事の配当機能付き ERC-20 トークンが継承できる形を想定して、ステーキングの機能を実装していきます。
この投稿について
この投稿では「ステーキングというと難しい処理をしているように感じる」という印象を「ステーキングの基本は『だれが・いつ・いくらステークしたのかを保持し、引き出す際に計算する』だけである」という印象に変えることを焦点にして解説しています。
最後のまとめで不動産についても言及していますので、「技術的な内容は難しい...」という方はまとめまで飛んでいただければと思います。
この投稿で扱うのは一つの実装例に過ぎないということにはご留意ください。実際にはステーキングの実装はこれよりも煩雑になり得ます。多くの場合、本質的にはステーキングには連想配列を利用します。連想配列とは特定のキー情報に対応するデータを保持できるプログラムの構造です。今回の場合はそのキーはウォレットアドレスになります。技術的には Solidity の場合は Mapping を利用することになりますが、アドレスに対応する「いつ・だれが・いくら」ステーキングをしたのかの情報が保存されます。この基本構造によってトークンが該当コントラクトに預けられ、配当とともに引き出せることになります。
トークンをスマートコントラクトに預ける
下記はこれからみていくコードの雛形になります。Stakable.sol のようなファイルを作り、トークンのコントラクトでインポートする想定です。
続いて、_addStakeholder
関数を作成します。こちらも内部から呼び出されることを想定されています。具体的には新規でステークする人の情報をコントラクト内部で作成するための関数です。
ガス代を安くするために Loop を使わずにインデックスを持たせ、値を直接参照できる様にしています。
続いて、ステーキング関数です。
ここで注意すべきことは、ここまでの関数ではどの口座の残高も変更していないことです。また、こちらはスマートコントラクト内部から呼び出されることを想定しています。例えば、100トークンしか持っていないのに1000トークンをステーキングしようとすると問題が起きますよね。そのチェックはトークンのコントラクトから行う必要があります。このコードは継承されることを念頭に書かれており、外部に晒されることになる stake
関数はトークンの実装本体で実装することになります。
そのため次に配当付きトークンの中に外部に公開される以下の関数を実装します。
ステークした人に報酬を与え、引き出させる
上記の実装で無事にステーキングの実装は完了です。しかしこれではただお金を預けるだけのコントラクトです。実際にはトークンを預けたことによる報酬がなければステークをするモチベーションは生まれません。続いて報酬の仕組みを考えていきます。
ここまでの実装は、ステーキングをすると持っていたトークンがバーンされ、「バーンされた分のトークンがステークされた」という事実が記録としてマッピングに記録されるところまででした。今度はかけた額をトークンのコントラクトに呼び戻す部分を実装します。
上記は withdrawStake
をトークンのコントラクトに実装したものになります。こちらも stake
関数と同じく外部に公開されます。こちらの関数では _withdrawStake
で諸々のチェックと配列およびマッピングの値を操作した後に _mint
関数を実行し、預けた金額に報酬を加えた額の通貨が発行されます。
それではこの操作の本質である _withdrawStake
とそれに関連する設定をみていきます。
報酬率と報酬計算
今回は時間当たり 0.5% の報酬を与えるように設定します。しかしこの報酬は永遠に 0.5% とは限りませんので、値は報酬率制御のために可変であり再代入可能である必要があります。Solidity の場合少数を扱いませんので、この場合は以下の様に変数に 5000 を代入しています(×0.005 は ÷5000と同等)。
この数字をベースに calculateReward
を実装します。この計算は内部の状態を何も変化させないため、view 修飾子もあるため、お金を消費せずに実行することが可能です。
今回の場合時間当たりの報酬は 0.5% になっていますが、以下のアルゴリズムでは便宜的に一律のリワードで計算するようにしています。ここは償還期間を定めたステークを適宜提供したり、ステーク開始時の値をコントラクト内部に保持することによって柔軟に実装することができます。
報酬の引き出し
ここまで、「預け入れ」と「報酬計算」そして「報酬を引き出すためのトークン側のインタフェース」を実装してきました。引き出し時には「だれが・いつ・いくら」預け入れをしたのかというデータにアクセスする必要があるほか、「そのステークが何番目にユーザーにとってステークされたものなのか」にも留意する必要があります。そのため、引き出す際には明示的に「何番目」のインデックスなのかを示してあげる必要があります。
まとめ
今回はステーキングを実装していきましたが、印象は「ステーキングというと難しい処理をしているように感じる」から「ステーキングの基本は『だれが・いつ・いくらステークしたのかを保持し、引き出す際に計算する』だけである」に変わりましたでしょうか。
ステーキングというと銀行にお金を預けて利子をもらうことを想像しがちですが、実装においてステーキングはトークンの移動ではなくトークンのバーンとミントで表現されます。一つの理由はその方がガス代が安いからでもありますが、この点は暗号資産の取引においては少し考え方を変える必要があるところです。トークンはそれ自体が造幣局になるので、実際には日銀自体にお金を預けたら、報酬を含め得た新券が発行されるイメージに近いです。
この仕組みはインフレ率に合わせてバリデーターの人に報酬を与えるために実装されていますが、高すぎる報酬設計はトークンの総供給量を著しく増やす結果になり、インフレを引き起こします。そのためバランスを取るためにデフレを同時に起こすために運営側でトークンのバーンをすることになりますが、その匙加減は議論が分かれます。手数料をバーンすることは総供給量を制限することにつながりますが、トークンの流動性によってはインフレ圧力の方が強くなってしまうことが考えられます。
不動産分野での活用
一般的にトークン自体はERC-20などの標準に沿ったプログラム群でしかないため、そのトークンは不動産持分を表すこともできますし、ガバナンストークンにもなり得ます。その他の例を挙げると ○○ to Earn で代表的な STEPN は GMT と GST という異なる代替可能トークンを用いて価格の調整を行ってエコシステムのバランスをとっています。このように同じような仕組みのトークンが違う目的で使われることになるため、ステーキングの実装事態もそのトークンの特徴や総供給量、流動性に合わせて適切なものが選ばれる必要があります。
RealT や Landshare のような不動産トークンに関しては、多くの場合で総供給量が固定になっているケースが多いことが観測できます。これは現実の不動産持分の概念と一致させていることや、そもそもの実売価格と対応させることなどが背景として挙げられます。この世界観に総供給量の変化の概念を持ち込むと概念が非常に煩雑になるでしょう。総供給量が限られるであろう不動産トークンにおいて、ステーキングなどの「預け入れにかかる報酬」のモデルを実現しながら、参画者によってガバナンスを効かせるためには何かしらの工夫が必要であると言えます。
当然のことながら、不動産トークンが特定のプラットフォームで実現された場合には、そのプラットフォーム自体は一般的なガバナンストークンのような代替可能トークンを持つことになります。それらのトークンが「特定の不動産を表すトークン」の所有者に対してどの様な報酬を設定するのかも一つの焦点になるでしょう。これがどのようなトークンのセットによって実現されるのか、またそれらが本当に “Web3 らしい” のかについては、今後の開発が待たれるところです。
* OSSで公開されているコードも解説に利用していますので、気になる方はそちらもご覧ください。