BuySingle function returning invalid proof - Solidity

Viewed 467

can someone help me out, I'm testing my buySingle function, by adding random values inside my first 3 inputs and an array in the last one. when I try executing I keep getting invalid proof. What I am supposed to be adding in the proof??

Currently, this is my _proof input:

[
"0x4406d4944cd8815a388db585f5a7a0b70d21ea3dd912414d4313136a2eac33a0",
"0x79fd2a4dca72d9577193fcc3d1ffd96ecf8127dab0bd295a48fcefa78c1ff948",
"0x6ec8df7eec81d5a31e79f9d3101bc2468ecf58af264968ba019766f12b2c6a65",
"0xb350ca80e5d4284caedb2d59caa85729952df9902c07f2d86054b05a4193551c",
"0xba0b16b6e0abae3d2a33a072dc7a0a59d77b1de708c5477e50634319f1496529",
"0x6c9b99e2e6f4aaaad0694375728a68e44160888e9fada56d440f4fbe3cbf33ea",
"0x17fb11285cd87a16370a061e2359f85d288f377f69248e7172c43475b7035806",
"0x33a63c21e96c9b7cc2081da21c9f2a0e8ff0904569a3acb17fa76129443dd047",
"0x266505b770c75759218c46c22f531ef5bd216c97246bf226567ddb753bfc93db",
"0x77e2eac49c0478daeac4f6023434ade425b8c45d4150d0d5a2c8cea8555027d2",
"0xe28a06c8328b5534e8de2adff61ee2916b040307a2165adab2cfd4b8eaa8fbfc",
"0x19a35f620df55ae0a87e54a8a1911bf9a40a5f41b54a1031f18a714c49e4ca17",
"0xd24be13d5d23f5102e312d8e5afd1b7dc5573b44e614c242d68d80899e10821f"
]

input:

price:
start:
end:
proof:

Output:

"message": "execution reverted: invalid proof"

and here is my code:

pragma solidity 0.8.10;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";
import "@openzeppelin/contracts/interfaces/IERC721.sol";
import "./IMintableERC721.sol";


contract MintablePresale is Ownable {
  // Use Zeppelin MerkleProof Library to verify Merkle proofs
    using MerkleProof for bytes32[];

  uint32 public nextId = 1;

  uint32 public finalId;

  uint32 public batchLimit;

  uint32 public mintLimit;

  uint32 public soldCounter;

  /**
   * @notice Merkle tree root to validate (address, cost, startDate, endDate)
   *         tuples
   */
  bytes32 public root;

    uint256 public constant UID = 0x96156164645b31243986cb90a4oio09d99f140aa39d43ffa58a6af6d5e90bdbf4;

  address public immutable tokenContract;

  address public immutable developerAddress;

  mapping(address => uint32) public mints;

  /**
   * @dev Fired in initialize()
   *
   * @param _by an address which executed the initialization
   * @param _nextId next ID of the token to mint
   * @param _finalId final ID of the token to mint
   * @param _batchLimit how many tokens is allowed to buy in a single transaction
   * @param _root merkle tree root
   */
  event Initialized(
    address indexed _by,
    uint32 _nextId,
    uint32 _finalId,
    uint32 _batchLimit,
    uint32 _limit,
    bytes32 _root
  );

  event Bought(address indexed _by, address indexed _to, uint256 _amount, uint256 _value);

  constructor(address _tokenContract, address _developerAddress) {
    // verify the input is set
    require(_tokenContract != address(0), "token contract is not set");

    // verify input is valid smart contract of the expected interfaces
    require(
      IERC165(_tokenContract).supportsInterface(type(IMintableERC721).interfaceId)
      && IERC165(_tokenContract).supportsInterface(type(IMintableERC721).interfaceId),
      "unexpected token contract type"
    );

    // assign the addresses
    tokenContract = _tokenContract;
    developerAddress = _developerAddress;
  }

  function itemsOnSale() public view returns(uint32) {
    // calculate items left on sale, taking into account that
    // finalId is on sale (inclusive bound)
    return finalId > nextId? finalId + 1 - nextId: 0;
  }

  function itemsAvailable() public view returns(uint32) {
    // delegate to itemsOnSale() if sale is active, return zero otherwise
    return isActive() ? itemsOnSale(): 0;
  }

  function isActive() public view virtual returns(bool) {
    // evaluate sale state based on the internal state variables and return
    return nextId <= finalId;
  }

  /**
   * @dev Restricted access function to set up sale parameters, all at once,
   *      or any subset of them
   *
   * @dev To skip parameter initialization, set it to `-1`,
   *      that is a maximum value for unsigned integer of the corresponding type;
   *      `_aliSource` and `_aliValue` must both be either set or skipped
   *
   * @dev Example: following initialization will update only _itemPrice and _batchLimit,
   *      leaving the rest of the fields unchanged
   *      initialize(
   *          0xFFFFFFFF,
   *          0xFFFFFFFF,
   *          10,
   *          0xFFFFFFFF
   *      )
   *
   * @dev Requires next ID to be greater than zero (strict): `_nextId > 0`
   *
   * @dev Requires transaction sender to have `ROLE_SALE_MANAGER` role
   *
   * @param _nextId next ID of the token to mint, will be increased
   *      in smart contract storage after every successful buy
   * @param _finalId final ID of the token to mint; sale is capable of producing
   *      `_finalId - _nextId + 1` tokens
   *      when current time is within _saleStart (inclusive) and _saleEnd (exclusive)
   * @param _batchLimit how many tokens is allowed to buy in a single transaction,
   *      set to zero to disable the limit
   * @param _mintLimit how many tokens is allowed to buy for the duration of the sale,
   *      set to zero to disable the limit
   * @param _root merkle tree root used to verify whether an address can mint
   */

  function initialize(
    uint32 _nextId,  // <<<--- keep type in sync with the body type(uint32).max !!!
    uint32 _finalId,  // <<<--- keep type in sync with the body type(uint32).max !!!
    uint32 _batchLimit,  // <<<--- keep type in sync with the body type(uint32).max !!!
    uint32 _mintLimit,  // <<<--- keep type in sync with the body type(uint32).max !!!
    bytes32 _root  // <<<--- keep type in sync with the 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF !!!
  ) public onlyOwner {
    // verify the inputs
    require(_nextId > 0, "zero nextId");

    // no need to verify extra parameters - "incorrect" values will deactivate the sale

    // initialize contract state based on the values supplied
    // take into account our convention that value `-1` means "do not set"
    // 0xFFFFFFFFFFFFFFFF, 64 bits
    // 0xFFFFFFFF, 32 bits
    if(_nextId != type(uint32).max) {
      nextId = _nextId;
    }
    // 0xFFFFFFFF, 32 bits
    if(_finalId != type(uint32).max) {
      finalId = _finalId;
    }
    // 0xFFFFFFFF, 32 bits
    if(_batchLimit != type(uint32).max) {
      batchLimit = _batchLimit;
    }
    // 0xFFFFFFFF, 32 bits
    if(_mintLimit != type(uint32).max) {
      mintLimit = _mintLimit;
    }
    // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 256 bits
    if(_root != 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
      root = _root;
    }

    // emit an event - read values from the storage since not all of them might be set
    emit Initialized(
      msg.sender,
      nextId,
      finalId,
      batchLimit,
      mintLimit,
      root
    );
  }

  /**
   * @notice Buys several (at least two) tokens in a batch.
   *      Accepts ETH as payment and mints a token
   *
   * @param _amount amount of tokens to create, two or more
   */
  function buy(uint256 _price, uint256 _start, uint256 _end, bytes32[] memory _proof, uint32 _amount) public payable {
    // delegate to `buyTo` with the transaction sender set to be a recipient
    buyTo(msg.sender, _price, _start, _end, _proof, _amount);
  }

  /**
   * @notice Buys several (at least two) tokens in a batch to an address specified.
   *      Accepts ETH as payment and mints tokens
   *
   * @param _to address to mint tokens to
   * @param _amount amount of tokens to create, two or more
   */
  function buyTo(address _to, uint256 _price, uint256 _start, uint256 _end, bytes32[] memory _proof, uint32 _amount) public payable {
    // construct Merkle tree leaf from the inputs supplied
    bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _price, _start, _end));

    // verify proof
    require(_proof.verify(root, leaf), "invalid proof");

    // verify the inputs
    require(_to != address(0), "recipient not set");
    require(_amount > 1 && (batchLimit == 0 || _amount <= batchLimit), "incorrect amount");
    require(block.timestamp >= _start, "sale not yet started");
    require(block.timestamp <= _end, "sale ended");

    // verify mint limit
    if(mintLimit != 0) {
      require(mints[msg.sender] + _amount <= mintLimit, "mint limit reached");
    }

    // verify there is enough items available to buy the amount
    // verifies sale is in active state under the hood
    require(itemsAvailable() >= _amount, "inactive sale or not enough items available");

    // calculate the total price required and validate the transaction value
    uint256 totalPrice = _price * _amount;
    require(msg.value >= totalPrice, "not enough funds");

    // mint token to to the recipient
    IMintableERC721(tokenContract).mintBatch(_to, nextId, _amount);

    // increment `nextId`
    nextId += _amount;
    // increment `soldCounter`
    soldCounter += _amount;
    // increment sender mints
    mints[msg.sender] += _amount;

    // if ETH amount supplied exceeds the price
    if(msg.value > totalPrice) {
      // send excess amount back to sender
      payable(msg.sender).transfer(msg.value - totalPrice);
    }

    // emit en event
    emit Bought(msg.sender, _to, _amount, totalPrice);
  }

  /**
   * @notice Buys single token.
   *      Accepts ETH as payment and mints a token
   */
  function buySingle(uint256 _price, uint256 _start, uint256 _end, bytes32[] memory _proof) public payable {
    // delegate to `buySingleTo` with the transaction sender set to be a recipient
    buySingleTo(msg.sender, _price, _start, _end, _proof);
  }

  /**
   * @notice Buys single token to an address specified.
   *      Accepts ETH as payment and mints a token
   *
   * @param _to address to mint token to
   */
  function buySingleTo(address _to, uint256 _price, uint256 _start, uint256 _end, bytes32[] memory _proof) public payable {
    // construct Merkle tree leaf from the inputs supplied
    bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _price, _start, _end));

    // verify proof
    require(_proof.verify(root, leaf), "invalid proof");

    // verify the inputs and transaction value
    require(_to != address(0), "recipient not set");
    require(msg.value >= _price, "not enough funds");
    require(block.timestamp >= _start, "sale not yet started");
    require(block.timestamp <= _end, "sale ended");

    // verify mint limit
    if(mintLimit != 0) {
      require(mints[msg.sender] + 1 <= mintLimit, "mint limit reached");
    }

    // verify sale is in active state
    require(isActive(), "inactive sale");

    // mint token to the recipient
    IMintableERC721(tokenContract).mint(_to, nextId);

    // increment `nextId`
    nextId++;
    // increment `soldCounter`
    soldCounter++;
    // increment sender mints
    mints[msg.sender]++;

    // emit en event
    emit Bought(msg.sender, _to, 1, _price);
  }

  }
}

I found this on the open zeppelin website

https://forum.openzeppelin.com/t/what-to-provide-in-the-proof-parameter-of-the-verify-function-for-the-merkleproof-library/3111

but still do not understand how to get the hashes from the leaf to the root.

Here is my IMintableERC721 as requested in the comments

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

interface IMintableERC721 {
    /**
     * @notice Checks if specified token exists
     *
     * @dev Returns whether the specified token ID has an ownership
     *      information associated with it
     *
     * @param _tokenId ID of the token to query existence for
     * @return whether the token exists (true - exists, false - doesn't exist)
     */
    function exists(uint256 _tokenId) external view returns(bool);

    /**
     * @dev Creates new token with token ID specified
     *      and assigns an ownership `_to` for this token
     *
     * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
     *      Prefer the use of `saveMint` instead of `mint`.
     *
     * @dev Should have a restricted access handled by the implementation
     *
     * @param _to an address to mint token to
     * @param _tokenId ID of the token to mint
     */
    function mint(address _to, uint256 _tokenId) external;

    /**
     * @dev Creates new tokens starting with token ID specified
     *      and assigns an ownership `_to` for these tokens
     *
     * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
     *
     * @dev n must be greater or equal 2: `n > 1`
     *
     * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
     *      Prefer the use of `saveMintBatch` instead of `mintBatch`.
     *
     * @dev Should have a restricted access handled by the implementation
     *
     * @param _to an address to mint tokens to
     * @param _tokenId ID of the first token to mint
     * @param n how many tokens to mint, sequentially increasing the _tokenId
     */
    function mintBatch(address _to, uint256 _tokenId, uint256 n) external;

    /**
     * @dev Creates new token with token ID specified
     *      and assigns an ownership `_to` for this token
     *
     * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
     *      `onERC721Received` on `_to` and throws if the return value is not
     *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
     *
     * @dev Should have a restricted access handled by the implementation
     *
     * @param _to an address to mint token to
     * @param _tokenId ID of the token to mint
     */
    function safeMint(address _to, uint256 _tokenId) external;

    /**
     * @dev Creates new token with token ID specified
     *      and assigns an ownership `_to` for this token
     *
     * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
     *      `onERC721Received` on `_to` and throws if the return value is not
     *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
     *
     * @dev Should have a restricted access handled by the implementation
     *
     * @param _to an address to mint token to
     * @param _tokenId ID of the token to mint
     * @param _data additional data with no specified format, sent in call to `_to`
     */
    function safeMint(address _to, uint256 _tokenId, bytes memory _data) external;

    /**
     * @dev Creates new tokens starting with token ID specified
     *      and assigns an ownership `_to` for these tokens
     *
     * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
     *
     * @dev n must be greater or equal 2: `n > 1`
     *
     * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
     *      `onERC721Received` on `_to` and throws if the return value is not
     *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
     *
     * @dev Should have a restricted access handled by the implementation
     *
     * @param _to an address to mint token to
     * @param _tokenId ID of the token to mint
     * @param n how many tokens to mint, sequentially increasing the _tokenId
     */
    function safeMintBatch(address _to, uint256 _tokenId, uint256 n) external;

    /**
     * @dev Creates new tokens starting with token ID specified
     *      and assigns an ownership `_to` for these tokens
     *
     * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
     *
     * @dev n must be greater or equal 2: `n > 1`
     *
     * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
     *      `onERC721Received` on `_to` and throws if the return value is not
     *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
     *
     * @dev Should have a restricted access handled by the implementation
     *
     * @param _to an address to mint token to
     * @param _tokenId ID of the token to mint
     * @param n how many tokens to mint, sequentially increasing the _tokenId
     * @param _data additional data with no specified format, sent in call to `_to`
     */
    function safeMintBatch(address _to, uint256 _tokenId, uint256 n, bytes memory _data) external;
}
0 Answers
Related