1

我正在研究智能合约以生成具有一些特定规则的随机卡,我试图使用 Chainlink VRF 生成随机数并实现这样的合约。

//SPDX-License-Identifier: Unlicense
pragma solidity >=0.7.4;

import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "../utils/Context.sol";
import "../utils/ReentrancyGuard.sol";
import "../utils/Address.sol";
import "../NFT/CardNFTAccessControls.sol";
import "../NFT/CardNFT.sol";

/**
 * @notice Factory contract for NFT handling payments on mint
 */
contract NFTFactory is Context, ReentrancyGuard, VRFConsumerBase {
  // using SafeMath for uint256;
  using Address for address payable;

  struct CardInfo {
    uint256 num;
    uint256 kind;
    uint256 value;
    uint256 qty;
  }

  struct NFTInfo {
    uint256 edition;
    uint256 cardInfoId;
  }

  struct RequestNFTConfig {
    uint256 limit;
    bool kind;
    uint256 id;
  }

  event NFTCreated(
    uint256 id,
    uint256 num,
    uint256 kind,
    uint256 edition
  );

  event RandomReturned(
    bytes32 requestId,
    uint256 randomness
  );

  /// @notice chainlink rng request value mapping
  mapping(bytes32 => RequestNFTConfig) private requestIds;

  /// @notice edition number id
  uint256 private editionId;

  /// @notice for switching off factory functionality
  bool public isPaused;

  /// @notice responsible for enforcing mint and admin role
  CardNFTAccessControls accessControls;

  /// @notice NFT contract
  CardNFT cardNFT;

  /// @notice platform  fee recipient address which will accept all fees and mint price
  address payable platformFeeRecipient;

  /// @notice keyhash for chainlink rng generator
  bytes32 internal keyHash;

  /// @notice fee for chainlink rng generator
  uint256 internal rngFee;

  /// @notice rng generator result
  uint256 public randomResult;

  /// @notice deck card information for calculation
  mapping(uint256 => CardInfo) deckInfo;

  /// @notice nft array
  mapping(uint256 => NFTInfo) nfts;

  modifier whenNotPaused() {
    require(!isPaused, "Function is currently paused");
    _;
  }

  constructor(
    CardNFTAccessControls _accessControls,
    CardNFT _cardNFT,
    address payable _platformFeeRecipient
  ) VRFConsumerBase(
    0xa555fC018435bef5A13C6c6870a9d4C11DEC329C,
    0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06
  ) {
    require(address(_accessControls) != address(0), "NFTFactory: Invalid Access Controls");
    require(address(_cardNFT) != address(0), "NFTFactory: Invalid NFT");
    require(_platformFeeRecipient != address(0), "NFTFactory: Invalid platform Fee recipient");
    
    accessControls = _accessControls;
    cardNFT = _cardNFT;
    platformFeeRecipient = _platformFeeRecipient;
    keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186;
    rngFee = 0.1 * 10 ** 18; // 0.1 LINK
  }

  /**
    @notice create one NFT
    @param _limit min or maximum number of card
    @param _kind limit kind with lower or higher
    @param _price price of NFT mint
    @param _amount amount of NFTs to mint
    @param _data additional data to leave
   */
  function create(uint256 _limit, bool _kind, uint256 _price, uint256 _amount, bytes memory _data) external payable nonReentrant whenNotPaused {
    require(_msgSender().isContract() == false, "Factory.create: No contracts are permitted");
    require(_msgSender() != address(0), "Factory.create: sender address is ZERO");
    uint256 buyPrice = msg.value;
    require(buyPrice >= _price, "Factory.create: payment should be same to original price");

    // (bool platformTransferSuccess,) = platformFeeRecipient.call{value : buyPrice}("");
    // require(platformTransferSuccess, "Factory.mintPayment: Failed to send platform fee");

    address creator = _msgSender();
    if(_amount > 1) {
      uint256[] memory _ids = cardNFT.batchMint(creator, _amount, _data);
      require(_amount == _ids.length, "Factory.create: should batch mint same amount of nfts");
      for(uint256 i=0; i<_ids.length; i++) {
        getRandomNumber(_ids[i], _limit, _kind);
      }
    } else {
      uint256 id = cardNFT.mint(creator, _data);
      getRandomNumber(id, _limit, _kind);
    }
  }

  /**
    @notice generate actual NFT info
    @param _requestId request id for chainlink rng
    @param _randomness random number which return from chainlink org
   */
  function generateNFTInfo(bytes32 _requestId, uint256 _randomness) private {
    RequestNFTConfig storage config = requestIds[_requestId];
    uint256 total = getTotalQtyToNumber(config.limit);
    bool _kind = config.kind;
    uint256 rngResult = 0;
    if(_kind) {
      rngResult = _randomness * (200000 - total) + total;
    } else {
      rngResult = _randomness * total;
    }
    uint256 cardInfoId = getNearestQtyCard(rngResult);
    NFTInfo storage nft = nfts[config.id];
    uint256 edition = _getNextEditionID();
    _incrementEditionId();
    nft.edition = edition;
    nft.cardInfoId = cardInfoId;
    CardInfo storage cardInfo = deckInfo[cardInfoId];

    emit NFTCreated(config.id, cardInfo.num, cardInfo.kind, edition);
  }

  /** 
    * Requests randomness 
    */
  function getRandomNumber(uint256 _id, uint256 _limit, bool _kind) private {
    require(LINK.balanceOf(address(this)) >= rngFee, "Not enough LINK - fill contract with faucet");
    bytes32 requestId = requestRandomness(keyHash, rngFee);
    RequestNFTConfig storage config = requestIds[requestId];
    config.id = _id;
    config.kind = _kind;
    config.limit = _limit;
  }

  /**
    * Callback function used by VRF Coordinator
    */
  function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    emit RandomReturned(requestId, randomness);
    generateNFTInfo(requestId, randomness);
  }

  ......
}

我在 BSC 测试网上试过这个,但没有RandomReturned事件日志,也没有更新 generateNFTInfo 函数。此外,我不确定如何在本地测试单元测试而不在本地安装 chainlink-node。有人可以帮我解决这个问题吗?

4

1 回答 1

-1

我以为我遇到了同样的问题,但是,我发现根据网络拥塞生成随机数可能需要 2 分钟以上。我正在 Rinkeby 网络中进行测试。

于 2021-11-27T23:31:34.280 回答