LoginSignup
0
1

【スマートコントラクト】独自トークンでNFTガチャ回せるようにしてみた

Last updated at Posted at 2022-09-20

ガチャを回すとNFTがランダムで当たるようなスマートコントラクトを書いたのでメモがてらQiitaにも書き殴ろうと思います。

概要

今回は独自コインであるERC20トークンを使うと1回ガチャができるようにします。

ガチャで当たるトークンはERC1155のトークンで4種類にします。
使うチェーンはETHのテストネット、Rinkebyです。fakeのETHをfaucet からもらってきてくださいね。

今回は確率は均一にします。
「トークンのレアリティを設計して、レアなトークンは確率を下げる」という実装も応用してやろうと思えばできますね。

そういえば 前書いた記事 でERC20トークンの実装やrinkeby、faucetについては書いているので今回はスキップします。同じ内容ですのでそちらを参照してください。

コード

ERC1155トークン & ガチャ


// SPDX-License-Identifier: MIT LICENSE

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/utils/Strings.sol";


pragma solidity ^0.8.0;

contract Emaki is ERC1155, Ownable {
    string baseMetadataURIPrefix;
    string baseMetadataURISuffix;
    using Strings for uint256;

    uint256 randNonce = 0;

    struct TokenInfo {
        IERC20 paytoken;
        uint256 costvalue;
    }
    uint256 degit_18 = 1000000000000000000
    address LWZaddress = 0x...的なデプロイしたERC20トークンのアドレスを入れる;
    IERC20 public LWZ = IERC20(LWZAddress);

    TokenInfo public AllowedCrypto = {
        LWZ,10*degit_18
};

    uint256[] public tokens = [
        0, 1, 2, 3
    ];

    string public name;
    string public symbol;


    constructor() ERC1155("") {

        name = "LowzzyGacha";
        symbol = "LGacha";
    }


    function uri(uint256 _id) public view override returns (string memory) {
        return string(abi.encodePacked(
            baseMetadataURIPrefix,
            Strings.toString(_id),
            baseMetadataURISuffix
        ));
    }
    
    # ガチャを回すときはこの関数を呼び出します。
    # _amount : 何回ガチャを回すか
    # requireは例外処理の記述で、rails でいうところのraiseです。
    function gacha(uint256 _amount) public payable {
        TokenInfo storage tokens = AllowedCrypto;
        IERC20 paytoken;
        paytoken = tokens.paytoken;
        uint256 cost;
        cost = tokens.costvalue * _amount;
        uint256 allowCost = paytoken.allowance(msg.sender,address(this));
        require(allowCost >= cost * _amount, "Not enough balance to complete transaction");
        paytoken.transferFrom(msg.sender, address(this), cost);

        uint256 num;
        uint256 id;
        for (uint256 i = 1; i <= _amount; i++) {
            randomNum = getRandomNum(msg.sender);
            uint256 len = tokens.length;
            uint256 id = randomNum % len;
            uint256 mintAmount = 1;
            _mint(msg.sender, id, mintAmount, "");
        }
    }

   # 今回の実装のキモである擬似乱数生成部分。
     # ブロックチェーンはその性質上乱数は予測しやすいため、
     # 十分に注意して生成する必要があります。
   # keccak 256を用いてtransactionやblockの情報から擬似乱数を生成しています。
     # ガチャが同時に複数回回された時、_to、block.timestampは同じ数値が入ってしまうため
     # randNonceを用いて同じ数値が入らないようにしています。
    function getRandomNum(address _to) public returns (uint256){
        randNonce++;

        uint256 randomNum = uint256(
            keccak256(
                abi.encode(
                    _to,
                    block.timestamp,
                    randNonce
                )
            )
        );
        return randomNum;
    }

     # ガチャをする際に支払われたトークンはスマートコントラクトのアドレスに
   # 貯まっていきます。
   # この関数はそのコントラクトに貯まったERC20トークンを引き出す関数です。
    function withdraw() public payable onlyOwner() {
        TokenInfo storage tokens = AllowedCrypto;
        IERC20 paytoken;
        paytoken = tokens.paytoken;
        paytoken.transfer(msg.sender, paytoken.balanceOf(address(this)));
    }
}


デプロイ

まず初めにERC20トークンをデプロイします。

前回と同じようにremixを使います。

デプロイしたら、ERC20トークンのコントラクトアドレスを

address LWZaddress = 0x...的なデプロイしたERC20トークンのアドレスを入れる;

に入れてみてください。

入れたらガチャスマートコントラクトをデプロイしてください。

ガチャトークンをデプロイしたら、erc20トークンであるLWZをガチャスマートコントラクトにapproveします。

その後、gacha(1)とでもすれば1回ガチャが回せますね。

ちょいと眠いので一旦おわり。最後の方が雑になってしまったので今度加筆修正をしますね!

それでは!

0
1
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
0
1