イーサリアムのDAppsをマネタイズするコード実例です!
こちらにあるのは最低限必要な内容のみですが、ビジネスモデルを考案する参考にしていただけると幸いです。
また法規制面で日本では不可なものもあるかと思いますが、あくまで技術面での参考資料としてお使いください。
注: 以下の例はすべて、マネタイズ部分の実装方法をわかりやすく示すために簡略化されれています。実際にプロダクトをリリースする場合は、必ずコードのセキュリティ・オーディットを実行しましょう!
(※この記事は、Mediumのイーサリアム上DAppsをマネタイズする6つの方法 Part 2をもとに作成されています。)
#まずはwithdraw関数!
はじめにオーナーのアドレスに資金を引き出せるようにしておきます。
function withdraw external onlyOwner {
owner.transfer(this.balance);
}
この関数は、非常に一般的なonlyOwner修飾子を使うパターンで、呼び出し元のコントラクトの残高を所有者のアドレスに転送します。
(上記にあるキーワードをよく知らない場合、まずはCryptoZombies.ioでのレッスンを完了してからまたこの記事を読み始めましょう!)
#1. クラウドセール作成 / トークン発行
イーサリアム公式ガイドで詳細に説明されています。
https://ethereum.org/crowdsale
クラウドセールのコントラクトは特に攻撃されやすいものなので、安全に行うためにもこちらの使用が推奨されます↓
Open Zeppelinのオーディットコントラクト
https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/crowdsale
追加でこちらもご参考ください。
https://blog.zeppelin.solutions/how-to-create-token-and-initial-coin-offering-contracts-using-truffle-openzeppelin-1b7a5dae99b6
#2. プレミアム機能/ゲーム内課金でレベルアップ
pragma solidity ^0.4.18;
contract ZombieLevelUp {
struct Zombie {
uint32 level;
uint32 winCount;
}
Zombie public zombie;
function levelUpZombie() payable public {
require (msg.value == 1 ether || zombie.winCount >= 10 * zombie.level);
zombie.winCount = 0;
zombie.level++;
}
function winBattle() public {
zombie.winCount++;
}
}
(このコードをすぐテストできるEthFiddleリンクはこちら)
上のようにすると、ユーザーは1 ether
を支払うことでレベルアップのwinCount
の要件をスキップすることが可能になります。そしてゲームの所有者は、withdraw
関数を呼び出すことで支払われたEtherを引き出すことができます。
ここで注意したいのは、Etherの価格は将来的に大幅に上昇(または下降)する可能性があるということです。そのため機能利用に必要となるコストを将来適切に変更できるよう、onlyOwner
関数を追加しておくことが大事になります。そうしなければ、アプリの利用が高コストになりすぎてしまい、誰も使えなくなってしまうかもしれません。
以下の例でも、コストを変更できるようにしておくことをお勧めします。
#3. 取引手数料のパーセンテージ
この例ではデジタルアセットのゾンビの売買を取り上げます。
誰かがゾンビを購入する場合、その価格の10%をあなたのウォレットに、残りをゾンビの元々の所有者に送金しています。
pragma solidity ^0.4.18;
contract SellZombie {
struct Zombie {
address owner;
uint32 dna;
}
Zombie public zombie;
address public owner;
uint256 public zombiePrice = 0.1 ether;
/// @dev constructor
function SellZombie(address _zombieOwner) public {
owner = msg.sender;
zombie.owner = _zombieOwner;
}
function buyZombie() payable public {
require(msg.value == zombiePrice);
// Can omit the following line if transferring all at once with withdraw
owner.transfer(msg.value/10);
zombie.owner.transfer((9 * msg.value)/10);
zombie.owner = msg.sender;
}
function withdraw() external {
owner.transfer(this.balance);
}
}
(このコードをすぐテストできるEthFiddleリンクはこちら)
売買があるたびにDApp所有者に送金しているとガスコストが多くかかってしまうので、これを節約するためにowner.transfer(msg.value/10);
をスキップして、代わりにEther
をコントラクトの残高として残しておくことができます。
そして前に説明したwithdraw関数を使って、後ほどまとめてコントラクトに含まれるすべてのEtherを引き出せます。
#4. サブスクリプション/ メンバーシップ
この取り入れ方としては3つが考えられます。
①終身
②時間ベース
③使用ベース
以下でそれぞれ見ていきましょう。
##①終身メンバーシップ
ここではシンプルに、ブーリアンのmapping
、ブーリアンが真であるかをチェックするmodifier
、そしてユーザーにある価格でメンバーになることを許可する関数を作成しています。
pragma solidity ^0.4.18;
contract LifetimeMembership {
bool public win;
mapping (address => bool) public members;
function becomeMember() external payable {
require(msg.value == 1 ether);
members[msg.sender] = true;
}
function winGame() external onlyMember {
win = true; // do whatever you want
}
modifier onlyMember() {
require(members[msg.sender] == true);
_;
}
}
(このコードをすぐテストできるEthFiddleリンクはこちら)
またメンバー/非メンバーにブール値を使用する代わりに、uint8
を使用してクラスの異なるメンバーシップにより多くの機能を持たせることもできまず。例えば無料ユーザーの場合はクラス0、シルバーメンバーシップの場合はクラス1、ゴールドメンバーシップの場合はクラス2、といった感じです。
そのあと例えばシルバークラスだけとしたい場合だと、onlySilver()
という関数を作り、メンバーシップのクラスが>= 1
であるかをチェックすればOKです。
##②時間ベースのメンバーシップ/ サブスクリプション
例えばサブスクリプションが1日あたり0.005 Etherだとすると・・・
pragma solidity ^0.4.18;
contract Subscription {
bool public win;
/// @dev Timestamp of when Membership expires
mapping (address => uint) public subscriptionExpiration;
/// @dev Cost per day of membership
uint subscriptionFee = 0.005 ether;
function renewSubscription() external payable {
uint daysToAdd = msg.value / subscriptionFee;
uint currentExpiration = subscriptionExpiration[msg.sender];
// If their membership already expired...
if (currentExpiration < now) {
// ...use `now` as the starting point of their new subscription
currentExpiration = now;
}
subscriptionExpiration[msg.sender] = currentExpiration + daysToAdd * 1 days;
}
modifier onlyMember() {
require(now < subscriptionExpiration[msg.sender]);
_;
}
function winGame() external onlyMember {
win = true; // do whatever you want
}
}
(このコードをすぐテストできるEthFiddleリンクはこちら)
このケースではユーザーがrenewSubscription
関数を呼び出すと、必要であればsubscriptionExpiration
がnow
で初期化され、そのあとユーザーの支払額に応じた日数が増やされます。
onlyMember
修飾子は現在時間が期限日を過ぎていないかをチェックします。
##③使用ベースのメンバーシップ/使用ごとの支払い
設定価格でAPIコールを購入する方法と同様に、一括前払いでユーザーが関数コールを購入することになります。
この例では、ユーザーは1 Etherあたり1000コールを購入できるようになっています。
ユーザーがonlyIfEnoughCalls
修飾子を持つ関数を呼び出すたびに、彼らが関数呼び出しに適格であることを確認した後availableCalls
変数をデクリメントしています。
pragma solidity ^0.4.18;
contract Subscription {
uint public wins;
mapping(address => uint) public availableCalls;
uint costPerCall = 0.001 ether;
function buyCalls() public payable {
uint callsPurchased = msg.value / costPerCall;
availableCalls[msg.sender] += callsPurchased;
}
modifier onlyIfEnoughCalls() {
require(availableCalls[msg.sender] > 0);
availableCalls[msg.sender]--;
_;
}
function winGame() public onlyIfEnoughCalls {
wins++; // do whatever you want
}
}
(このコードをすぐテストできるEthFiddleリンクはこちら)
ちなみにこの場合に限らず、only●●修飾子はどの関数にも加えることができるので、アクセス権を与えられたユーザーのみ関数を呼び出せるようにしたい場合に重宝します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/240034/fcaafdd9-54b7-992e-8273-88843a21cee1.png)
DApp開発に興味がありますか?私たちの完全無料のコースをチェックして、早速開発を始めましょう! ⬆
###フォローをお願いします!
Github | Telegram | Twitter | Medium
Visit us at https://loomx.io
Contact us: team@loomx.io