0

I am trying to write a test script using hardhat to test the purchaseSnack function but I keep receiving these errors.

 1) VendingMachine
       Should fail if insufficient funds are sent:
     TypeError: AbiCoder is not a constructor
      at decodeReturnData (node_modules/@nomicfoundation/hardhat-chai-matchers/src/internal/reverted/utils.ts:65:15)
      at onError (node_modules/@nomicfoundation/hardhat-chai-matchers/src/internal/reverted/revertedWith.ts:45:51)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at runNextTicks (node:internal/process/task_queues:64:3)
      at listOnTimeout (node:internal/timers:533:9)
      at processTimers (node:internal/timers:507:7)
      at Context.<anonymous> (test/VendingMachineTest.js:27:5)

  2) VendingMachine
       Should fail if invalid snack is selected:
     TypeError: AbiCoder is not a constructor
      at decodeReturnData (node_modules/@nomicfoundation/hardhat-chai-matchers/src/internal/reverted/utils.ts:65:15)
      at onError (node_modules/@nomicfoundation/hardhat-chai-matchers/src/internal/reverted/revertedWith.ts:45:51)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at runNextTicks (node:internal/process/task_queues:64:3)
      at listOnTimeout (node:internal/timers:533:9)
      at processTimers (node:internal/timers:507:7)
      at Context.<anonymous> (test/VendingMachineTest.js:35:5)

  3) VendingMachine
       Should fail if trying to purchase more snacks than available:
     TypeError: AbiCoder is not a constructor
      at decodeReturnData (node_modules/@nomicfoundation/hardhat-chai-matchers/src/internal/reverted/utils.ts:65:15)
      at onError (node_modules/@nomicfoundation/hardhat-chai-matchers/src/internal/reverted/revertedWith.ts:45:51)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at runNextTicks (node:internal/process/task_queues:64:3)
      at listOnTimeout (node:internal/timers:533:9)
      at processTimers (node:internal/timers:507:7)
      at Context.<anonymous> (test/VendingMachineTest.js:43:5)
    ```
    Here is my test script file
    ```javascript
    const { ethers } = require("hardhat")
const { expect, assert } = require("chai")

describe("VendingMachine", function () {
  let VendingMachineFactory, vendingMachine
  beforeEach(async function () {
    VendingMachineFactory = await ethers.getContractFactory("VendingMachine")
    ;[owner, addr1, addr2, ...addrs] = await ethers.getSigners()
    vendingMachine = await VendingMachineFactory.deploy({
      value: ethers.utils.parseEther("1.0"),
    })
    await vendingMachine.deployed()
  })

  it("Should reduce quantity of the snack and increment the balance of contract", async function () {
    const initialBalance = await vendingMachine.getBalance()
    await vendingMachine
      .connect(addr1)
      .purchaseSnack("chips", 1, { value: ethers.utils.parseEther("0.001") })
    const finalBalance = await vendingMachine.getBalance()
    expect(finalBalance).to.equal(
      initialBalance.add(ethers.utils.parseEther("0.001")),
    )
  })

  it("Should fail if insufficient funds are sent", async function () {
    await expect(
      vendingMachine.connect(addr1).purchaseSnack("chips", 1, {
        value: ethers.utils.parseEther("0.0005"),
      }),
    ).to.be.revertedWith("Declined! Insufficient payment amount.")
  })

  it("Should fail if invalid snack is selected", async function () {
    await expect(
      vendingMachine.connect(addr1).purchaseSnack("invalid", 1, {
        value: ethers.utils.parseEther("0.001"),
      }),
    ).to.be.revertedWith("Invalid snack selected")
  })

  it("Should fail if trying to purchase more snacks than available", async function () {
    await expect(
      vendingMachine.connect(addr1).purchaseSnack("chips", 21, {
        value: ethers.utils.parseEther("0.021"),
      }),
    ).to.be.revertedWith("We've sold out of this item!")
  })

  it("Should return the balance of the vending machine", async function () {
    const contractBalanceBefore = await vendingMachine.getBalance()
    const amountToSend = ethers.utils.parseEther("1.0")

    // Send some funds to the contract
    await owner.sendTransaction({
      to: vendingMachine.address,
      value: amountToSend,
    })

    const contractBalanceAfter = await vendingMachine.getBalance()
    expect(contractBalanceAfter).to.equal(
      contractBalanceBefore.add(amountToSend),
    )
  })
})

Here is my smart contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

// import "hardhat/console.sol";

contract VendingMachine {
    /* Errors */
    error VendingMachine__OverStock(string message);
    error VendingMachine__TransferFailed();
    /* State Variables */
    // use address(this).balance to find the contract's balance
    uint256 public constant INITIAL_QUANTITY = 20;
    uint256 contractBalance = address(this).balance;

    address private immutable i_owner;

    string[] private snackNames;

    struct Snacks {
        uint256 quantity;
        uint256 price;
    }
    mapping(string => Snacks) public inventory;
    // tracks balances of each caller that interacts with the contract
    mapping(address => uint256) public balances;
    /* Events */
    event Purchased(string msg, uint256 qtyPurchased, string snackPurchased);
    event WithdrawFunds(address ownerAccount, uint256 withdrawBalance);
    /*  Modifier */
    modifier onlyOwner() {
        require(msg.sender == i_owner, "Not contract owner");
        _;
    }

    constructor() payable {
        i_owner = msg.sender;
        inventory["chips"] = Snacks(INITIAL_QUANTITY, 0.001 ether);
        inventory["drinks"] = Snacks(INITIAL_QUANTITY, 0.002 ether);
        inventory["cookies"] = Snacks(INITIAL_QUANTITY, 0.001 ether);
        snackNames.push("chips");
        snackNames.push("drinks");
        snackNames.push("cookies");
    }

    // receive function to handle ether being sent
    receive() external payable {
        balances[msg.sender] += msg.value;
    }

    // Function that takes in ether payment in exchange for specific snack
    function purchaseSnack(string calldata _snack, uint256 _quantity)
        public
        payable
    {
        Snacks storage snack = inventory[_snack];
        uint256 snackPrice = snack.price; // Retrieve the price based on the snack name

        require(snackPrice > 0, "Invalid snack selected"); // Verify that the snack price exists
        require(
            msg.value >= snackPrice,
            "Declined! Insufficient payment amount."
        ); // Verify that the payment is sufficient

        require(snack.quantity >= _quantity, "We've sold out of this item!"); // Verify the quantity of snacks in the vending machine
        snack.quantity -= _quantity;

        balances[msg.sender] += snackPrice; // accounts for the amount spent from customer
        contractBalance += snack.price; // accounts for revenue earned by vending machine

        emit Purchased("Thank you for your purchase of ", _quantity, _snack);
    }

    // Function to withdraw funds
    function withdraw() public onlyOwner {
        uint256 _balance = address(this).balance;
        payable(i_owner).transfer(_balance);
      
        emit WithdrawFunds(i_owner, _balance);
    }

    // Function to restock the inventory
    // only the owner should be able to call this fn
    function restock() private onlyOwner {
        // update quantity of snacks
        for (uint256 i = 0; i < snackNames.length; i++) {
            string memory snackName = snackNames[i];
            Snacks storage snack = inventory[snackName];
            uint256 restockQuantity = INITIAL_QUANTITY - snack.quantity; // Checks if the restock quantity is greater than or equal to the needed quantity
            // checks whether the current snack quantity is less than the needed quantity
            if (snack.quantity < INITIAL_QUANTITY) {
                snack.quantity += restockQuantity;
            } else {
                revert VendingMachine__OverStock("No need to restock");
            }
        }
    }

    // Function to check the balance of the vending machine
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.