1

One of my hardhat tests is failing and I am hoping someone assist with. I have 2 smart contracts posted here and the test file I am running. My question is at the bottom. It's a bit lengthy to look through because there are 2 contracts and a test file but my question is pretty basic regarding approve and transferFrom failure in my test. A screen shot of the failure message from my console is posted. I realize this is a lot to look through so much respect to anyone who does and it is greatly appreciated.

CrowdCoin.sol (This is an ERC20 token I created that will be used for a CrowdFund)

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract CrowdCoin is ERC20 {
  uint constant _initial_supply = 100 * (10**18);
    
    constructor() ERC20("CrowdCoin", "CC") {
        _mint(msg.sender, _initial_supply);
    }

    function mint(uint amount) external {
        _mint(msg.sender, amount);
        emit Transfer(address(0), msg.sender, amount);
    }

    function burn(uint amount) external {
        _burn(msg.sender, amount);
        emit Transfer(msg.sender, address(0), amount);
    }
}

Here is the CrowdFund.sol contract. ERC20 Tokens from CrowdCoin (above) should be used in this contract...

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

//import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract CrowdFund {

    
    address payable public owner;

    ERC20 public immutable token;

    uint public count;

    mapping(uint => Campaign) public campaigns;

    mapping(uint => mapping(address => uint)) public pledgedAmount;

    constructor(address _token) payable {
        token = ERC20(_token);
        owner = payable(msg.sender);
    }

    struct Campaign {
        address creator;
        uint goal;
        uint pledged;
        uint32 startAt;
        uint32 endAt;
        bool claimed;
    }

    event Launch(uint id, address indexed creator, uint goal, uint32 startAt, uint32 endAt);
    event Cancel(uint id);
    event Pledge(uint indexed id, address indexed caller, uint amount);
    event Unpledge(uint indexed id, address indexed caller, uint amount);
    event Claim(uint _id);
    event Refund(uint indexed id, address indexed caller, uint amount);

    function launch(uint _goal, uint32 _startAt, uint32 _endAt) external {
        //require(_startAt >= block.timestamp, "start at < now");
        require(_endAt >= _startAt, "end at < start at");
        require(_endAt <= block.timestamp + 90 days, "end at > max duration");

        count += 1;
        campaigns[count] = Campaign({
            creator: msg.sender,
            goal: _goal,
            pledged: 0,
            startAt: _startAt,
            endAt: _endAt,
            claimed: false
        });

        emit Launch(count, msg.sender, _goal, _startAt, _endAt);
    }

    function cancel(uint _id) external {
        Campaign memory campaign = campaigns[_id];
        require(msg.sender == campaign.creator, "not creator");
        require(block.timestamp < campaign.startAt, "started");
        delete campaigns[_id];
        emit Cancel(_id);
    }

    function pledge(uint _id, uint _amount) external payable {
        Campaign storage campaign = campaigns[_id];
        require(block.timestamp >= campaign.startAt, "not started");
        require(block.timestamp <= campaign.endAt, "ended");

        campaign.pledged += _amount;
        pledgedAmount[_id][msg.sender] += _amount;
        token.transferFrom(msg.sender, address(this), _amount);

        emit Pledge(_id, msg.sender, _amount);
    }

    function unpledge(uint _id, uint _amount) external {
        Campaign storage campaign = campaigns[_id];
        require(block.timestamp <= campaign.endAt, "ended");

        campaign.pledged -= _amount;
        pledgedAmount[_id][msg.sender] -= _amount;
        token.transfer(msg.sender, _amount);

        emit Unpledge(_id, msg.sender, _amount);
    }

    //If the total amount pledged was > than goal then the creator can claim the tokens
    function claim(uint _id) external {
        Campaign storage campaign = campaigns[_id];
        require(msg.sender == campaign.creator, "not creator");
        require(block.timestamp > campaign.endAt, "not ended");
        require(campaign.pledged >= campaign.goal, "pledged < goal");
        require(!campaign.claimed, "claimed");

        campaign.claimed = true;
        token.transfer(msg.sender, campaign.pledged);

        emit Claim(_id);
    }

    function refund(uint _id) external {
        Campaign storage campaign = campaigns[_id];
        require(msg.sender == campaign.creator, "not creator");
        require(block.timestamp > campaign.endAt, "not ended");

        uint bal = pledgedAmount[_id][msg.sender];
        pledgedAmount[_id][msg.sender] = 0;
        token.transfer(msg.sender, bal);

        emit Refund(_id, msg.sender, bal);
    }
}

Here are the tests I am running....

const { expect, assert } = require('chai');
const { ethers } = require("hardhat");

describe('Token Contract()', () => {
    let CrowdFund, crowdfund, CrowdCoin, crowdcoin, owner, addr1, addr2;

    beforeEach(async () => {
        CrowdCoin = await ethers.getContractFactory("CrowdCoin");
        crowdcoin = await CrowdCoin.deploy();
        CrowdFund = await ethers.getContractFactory("CrowdFund");
        crowdfund = await CrowdFund.deploy(crowdcoin.address);

        [owner, addr1, addr2] = await ethers.getSigners();
    })

    describe('Deployment', () => {
        it('Should set the right owner', async () => {
            expect(await crowdfund.owner()).to.equal(owner.address);
         });

         it('Should allow addr1 to mint 12 crowdcoin tokens', async () => {
         const MINT_ETHER = ethers.utils.parseEther("12");
         await crowdcoin.connect(addr1).mint(MINT_ETHER);
         expect(await crowdcoin.balanceOf(addr1.address)).to.equal(ethers.utils.parseEther("12"));
         console.log(await crowdcoin.balanceOf(addr1.address));
        });

        it("Should allow under to launch a project", async () => {
            console.log(((new Date().getTime()/1000).toString()));
            const a = Math.floor(Number((new Date().getTime()/1000).toString()));
            const b = Math.floor(Number(((new Date().getTime()/1000) + 1000).toString()));
            const aNumber = Number(a);
            const bNumber = Number(b);
            console.log(aNumber);
            console.log(bNumber);       
            const ONE_ETHER = ethers.utils.parseEther("1");
            await crowdfund.connect(owner).launch(ONE_ETHER, aNumber, bNumber);
            console.log(await crowdfund.count());
            console.log(await crowdfund.campaigns(1));
            expect(await crowdfund.count()).to.equal("1");
          });
        it("Should allow addr1 to pledge tokens to campaign", async () => {
            const ONE_ETHER = ethers.utils.parseEther("1");
            let tx1 = await crowdcoin.connect(addr1).approve(crowdfund.address, ONE_ETHER);
            await tx1.wait();
            console.log("Transaction1:",tx1.hash);
            let tx2 = await crowdfund.connect(addr1).pledge(1, ONE_ETHER);
            await tx2.wait();
            console.log("Transaction1:",tx2.hash);
            const balance = await crowdfund.pledgedAmount(1, addr1.address);
            expect(await ethers.utils.parseEther(balance) == ethers.utils.parseEther("1"))
        });
    });
});

Contracts are successfully compiling and the first few tests pass but this is the error i am getting when i try to have addr1 pledge to a campaign....

enter image description here

I am not sure what the issue is because I minted 12 crowdcoins to addr1. I also approved crowdfund before I called the pledge function from addr1 to pledge to campaign 1. The pledge function calls transferFrom which is why I approved it crowdfund before calling the pledge function. So why would it be failing if addr1 actually owns 12 crowdcoin tokens and crowdfund.address is approved? Why is it saying he doesn't have the balance? Thanks again if you made it all the through.

1 Answer 1

1

The problem is with the beforeEach() function:

    beforeEach(async () => {
        CrowdCoin = await ethers.getContractFactory("CrowdCoin");
        crowdcoin = await CrowdCoin.deploy();
        CrowdFund = await ethers.getContractFactory("CrowdFund");
        crowdfund = await CrowdFund.deploy(crowdcoin.address);

        [owner, addr1, addr2] = await ethers.getSigners();
    })

When you use the beforeEach() function. It will execute the code inside the beforeEach() before each test case (it("") statement). So, when you are running the last test case it first deploys new smart contracts and then run it. Which makes the balance 0. The solution is to use the before() in place of beforeEach()

2
  • WOW! THANK YOU! I spent days trying to figure out what i was doing wrong. Great Catch! Works now. Greatly appreciate the help.
    – Blockpain
    Mar 26, 2023 at 10:09
  • You're very welcome! I'm glad to hear that I was able to help you out.
    – codeTIT4N
    Mar 26, 2023 at 18:51

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.