const { ethers, deployments, getNamedAccounts } = require("hardhat"); const { expect, assert } = require("chai"); describe("FundMe", function () { //// Test For The Constructor let fundMe; let deployer; let mockV3Aggregator; beforeEach(async () => { deployer = (await getNamedAccounts()).deployer; /* // Alternative To Above, Localhost will get 10 fake accounts const accounts = await ethers.getSigners(); const account1 = accounts[0]; const account2 = accounts[1]; */ await deployments.fixture(["all"]); fundMe = await ethers.getContract("FundMe", deployer); mockV3Aggregator = await ethers.getContract( "MockV3Aggregator", deployer, ); }); let eth1 = "1000000000000000000"; // 1 ETH eth1 = ethers.utils.parseEther("1"); // Same 1 ETH describe("constructor", async function () { it("Set the aggregator priceFeedAddress correctly", async function () { const response = await fundMe.getPriceFeed(); // AggregatorV3Interface //console.log("AggregatorV3Interface" + response); assert.equal(response, mockV3Aggregator.address); }); it("Updated the amount funded data structure", async function () { await fundMe.fund({ value: eth1 }); const response = await fundMe.getAddressToAmountFunded(deployer); assert.equal(response.toString(), eth1.toString()); // Send 1 ETH }); it("Adds funder to array of funders", async function () { await fundMe.fund({ value: eth1 }); const funder = await fundMe.getFunders(0); assert.equal(funder, deployer); }); }); // Test for the fund() function describe("fund function", async function () { it("Fail if you don't send enough ETH", async function () { await expect(fundMe.fund()).to.be.revertedWith( "Didn't send enough", ); }); }); // Test For The receive() function let FundMe; let owner; let addr1; let addr2; let addrs; beforeEach(async function () { // Get the ContractFactory and Signers here. [owner, addr1, addr2, ...addrs] = await ethers.getSigners(); // Deploy Mock Price Feed const MockV3Aggregator = await ethers.getContractFactory("MockV3Aggregator"); mockV3Aggregator = await MockV3Aggregator.deploy(8, 2000 * 1e8); // Deploy FundMe contract FundMe = await ethers.getContractFactory("FundMe"); fundMe = await FundMe.deploy(mockV3Aggregator.address); await fundMe.deployed(); }); describe("receive function", function () { it("should call the fund function when receiving Ether", async function () { // Send ETH to the contract await addr1.sendTransaction({ to: fundMe.address, value: ethers.utils.parseEther("1.0"), // 1 ETH }); // Check if addr1 is added to funders expect(await fundMe.getFunders(0)).to.equal(addr1.address); // Check if the amount funded is recorded expect( await fundMe.getAddressToAmountFunded(addr1.address), ).to.equal(ethers.utils.parseEther("1.0")); }); it("should revert if the amount sent is below the minimum funding value", async function () { // 1 USD = 0.0005 ETH (Assuming 1 ETH = 2000 USD) const minFunding = ethers.utils.parseEther("0.0005"); // 0.5 USD await expect( addr1.sendTransaction({ to: fundMe.address, value: minFunding.sub(1), // Slightly less than the minimum }), ).to.be.revertedWith("Didn't send enough"); }); it("should accept funds greater than or equal to the minimum funding value", async function () { // 50 USD = 0.025 ETH (Assuming 1 ETH = 2000 USD) const minFunding = ethers.utils.parseEther("0.025"); await expect( addr1.sendTransaction({ to: fundMe.address, value: minFunding, // Exactly the minimum }), ).not.to.be.reverted; expect(await fundMe.getFunders(0)).to.equal(addr1.address); expect( await fundMe.getAddressToAmountFunded(addr1.address), ).to.equal(minFunding); }); }); describe("withdraw", async function () { beforeEach(async function () { await fundMe.fund({ value: eth1 }); // Add 1 ETH first }); it("Withdraw ETH from a single funder", async function () { /* * Deployer = funder * fundMe and replace with ethers, important to getBalance */ // Arrange const startingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const startingDeployerBalance = await fundMe.provider.getBalance(deployer); // Act const transactionResponse = await fundMe.withdraw(); const transactionReceipt = await transactionResponse.wait(1); const endingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const endingDeployerBalance = await fundMe.provider.getBalance(deployer); const { gasUsed, effectiveGasPrice } = transactionReceipt; //gasUsed = "0x8b14"; effectiveGasPrice = "0x4e4814ec"; const gasCost = gasUsed.mul(effectiveGasPrice); // Assert assert.equal(endingFundMeBalance, 0); assert.equal( startingFundMeBalance.add(startingDeployerBalance), endingDeployerBalance.add(gasCost).toString(), ); }); it("Allow us to withdraw with multiple funders", async function () { const accounts = await ethers.getSigners(); for (let a = 0; a < 6; a++) { const fundMeConnectedContract = await fundMe.connect( accounts[a], ); await fundMeConnectedContract.fund({ value: eth1 }); } const startingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const startingDeployerBalance = await fundMe.provider.getBalance(deployer); // Act const transactionResponse = await fundMe.withdraw(); // Let's comapre gas costs :) // const transactionResponse = await fundMe.withdraw() const transactionReceipt = await transactionResponse.wait(); const { gasUsed, effectiveGasPrice } = transactionReceipt; const withdrawGasCost = gasUsed.mul(effectiveGasPrice); console.log(`GasCost: ${withdrawGasCost}`); console.log(`GasUsed: ${gasUsed}`); console.log(`GasPrice: ${effectiveGasPrice}`); const endingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const endingDeployerBalance = await fundMe.provider.getBalance(deployer); // Assert assert.equal( startingFundMeBalance.add(startingDeployerBalance).toString(), endingDeployerBalance.add(withdrawGasCost).toString(), ); // Make a getter for storage variables await expect(fundMe.getFunders(0)).to.be.reverted; for (i = 1; i < 6; i++) { assert.equal( await fundMe.getAddressToAmountFunded(accounts[i].address), 0, ); } }); it("Only allow the owner to withdraw", async function () { const accounts = await ethers.getSigners(); const attacker = accounts[1]; const attacherConnectedContract = await fundMe.connect(attacker); await expect(attacherConnectedContract.withdraw()).to.be.reverted; await expect( attacherConnectedContract.withdraw(), ).to.be.revertedWith("FundMe__NotOwner"); }); }); describe("cheaperWithdraw", async function () { beforeEach(async function () { await fundMe.fund({ value: eth1 }); // Add 1 ETH first }); it("Withdraw ETH from a single funder", async function () { /* * Deployer = funder * fundMe and replace with ethers, important to getBalance */ // Arrange const startingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const startingDeployerBalance = await fundMe.provider.getBalance(deployer); // Act const transactionResponse = await fundMe.cheaperWithdraw(); const transactionReceipt = await transactionResponse.wait(1); const endingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const endingDeployerBalance = await fundMe.provider.getBalance(deployer); const { gasUsed, effectiveGasPrice } = transactionReceipt; //gasUsed = "0x8b14"; effectiveGasPrice = "0x4e4814ec"; const gasCost = gasUsed.mul(effectiveGasPrice); // Assert assert.equal(endingFundMeBalance, 0); assert.equal( startingFundMeBalance.add(startingDeployerBalance), endingDeployerBalance.add(gasCost).toString(), ); }); it("Allow us to withdraw with multiple funders", async function () { const accounts = await ethers.getSigners(); for (let a = 0; a < 6; a++) { const fundMeConnectedContract = await fundMe.connect( accounts[a], ); await fundMeConnectedContract.fund({ value: eth1 }); } const startingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const startingDeployerBalance = await fundMe.provider.getBalance(deployer); // Act const transactionResponse = await fundMe.cheaperWithdraw(); // Let's comapre gas costs :) // const transactionResponse = await fundMe.cheaperWithdraw() const transactionReceipt = await transactionResponse.wait(); const { gasUsed, effectiveGasPrice } = transactionReceipt; const withdrawGasCost = gasUsed.mul(effectiveGasPrice); console.log(`GasCost: ${withdrawGasCost}`); console.log(`GasUsed: ${gasUsed}`); console.log(`GasPrice: ${effectiveGasPrice}`); const endingFundMeBalance = await fundMe.provider.getBalance( fundMe.address, ); const endingDeployerBalance = await fundMe.provider.getBalance(deployer); // Assert assert.equal( startingFundMeBalance.add(startingDeployerBalance).toString(), endingDeployerBalance.add(withdrawGasCost).toString(), ); // Make a getter for storage variables await expect(fundMe.getFunders(0)).to.be.reverted; for (i = 1; i < 6; i++) { assert.equal( await fundMe.getAddressToAmountFunded(accounts[i].address), 0, ); } }); it("Only allow the owner to withdraw", async function () { const accounts = await ethers.getSigners(); const attacker = accounts[1]; const attacherConnectedContract = await fundMe.connect(attacker); await expect(attacherConnectedContract.cheaperWithdraw()).to.be .reverted; await expect( attacherConnectedContract.cheaperWithdraw(), ).to.be.revertedWith("FundMe__NotOwner"); }); }); });