LoginSignup
16
17

More than 5 years have passed since last update.

DAppsのマネタイズサンプルコード

Last updated at Posted at 2018-05-28

イーサリアムの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関数を呼び出すと、必要であればsubscriptionExpirationnowで初期化され、そのあとユーザーの支払額に応じた日数が増やされます。
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

DApp開発に興味がありますか?私たちの完全無料のコースをチェックして、早速開発を始めましょう! ⬆

フォローをお願いします!

Github | Telegram | Twitter | Medium
Visit us at https://loomx.io
Contact us: team@loomx.io

16
17
0

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
16
17