0

this is my first post here.

error (see how the prank "...cb2", is not setting the msg.sender?)

enter image description here

I am making a Solidity NFT Whitelist mint project using Merkle Tree hashing. I generate Merkle Tree hashes off chain and the whitelisted persons address is allowed to mint based on the proof[] that they provide.

My problem is that I am not sure how Foundry Testing deals with msg.sender. My test is to test wether the person initiating a mint function is on my whitelist. I know the proofs and addresses work with the mint function on Remix which I have tested manually.

In Foundry I am using SetUp() to load the constructor with my "BaseURI" and "MerkleRoot".

Then I am testing the mint function with a loop that goes through my whitelistAddresses[i] and trys to mint each time with proofs[i] as each address is associated with a specific proof[i].

For each loop I expect to set the msg.sender to the whitelistAddress[i], but the prank is not working as expected. I continually get the error.

How do I change msg.sender to my whitelistAddress[i] in my test_mint function? Or am I thinking about this wrong.

Here is the error output above where you can see that I try to prank my "...cb2" address as the msg.sender and then immediately log the "msg.sender" and it still shows up as the "...f38" DefaultSender address that comes with Foundry.

Is my test just wrong? Am I just doing something Nooby? How can I test my mint function? I think I almost have it, its just the msg.sender that would be the "address trying to mint" isnt being set to the actual address from my whitelist.

Any help is appreciated!

Below is my test file where I am trying to test the mint function in Coolios.

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

import {Test, console} from "../lib/forge-std/src/Test.sol";
import {Coolios} from "../src/Coolios.sol";
import "../lib/forge-std/src/StdCheats.sol";

contract CooliosFuzzTest is Test {
    Coolios public coolios;

    address[] public whitelistAddresses = [
        0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,
        0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db,
        0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB,
        0x617F2E2fD72FD9D5503197092aC168c91465E7f2
    ];

   bytes32[][] public proofs = [
    [
        bytes32(0x04a10bfd00977f54cc3450c9b25c9b3a502a089eba0097ba35fc33c4ea5fcb54),
        bytes32(0xda2a605bdf59a3b18e24cd0b2d9110b6ffa2340f6f67bc48214ac70e49d12770)
    ],
    [
        bytes32(0x999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb),
        bytes32(0xda2a605bdf59a3b18e24cd0b2d9110b6ffa2340f6f67bc48214ac70e49d12770)
    ],
    [
        bytes32(0xf6d82c545c22b72034803633d3dda2b28e89fb704f3c111355ac43e10612aedc),
        bytes32(0x39a01635c6a38f8beb0adde454f205fffbb2157797bf1980f8f93a5f70c9f8e6)
    ],
    [
        bytes32(0xdfbe3e504ac4e35541bebad4d0e7574668e16fefa26cd4172f93e18b59ce9486),
        bytes32(0x39a01635c6a38f8beb0adde454f205fffbb2157797bf1980f8f93a5f70c9f8e6)
    ]
];


    
  //  CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    function setUp() public {
        string memory baseURI = "https://example.com/";
        bytes32 merkleRoot = 0xfbaa96a1f7806c1ab06f957c8fc6e60875b6880254f77b71439c7854a6b47755;
        coolios = new Coolios(baseURI, merkleRoot);
    }

    //tests for the contract below
    function testNameIsCoolios() public view {
        assertEq(coolios.name(), "Coolios");
    }

    //test for the msg.sender
    function test_MintingCoolios() public{
        for (uint256 i = 0; i <= whitelistAddresses.length; i++) {
        
        vm.deal(whitelistAddresses[i], 1);
        vm.prank(whitelistAddresses[i]);

        emit log_address(msg.sender);
        coolios.mint{value: 0.1 ether}(proofs[i],1);
        
        assertEq(coolios.totalSupply(), 1);
     
        
        
        
        }
    }
}

The full contract is here if you want to look at the mint function that we are testing.

// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.0;

import "lib/ERC721A/contracts/ERC721A.sol";

import "lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
import "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import "lib/openzeppelin-contracts/contracts/utils/Strings.sol";
import "lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol";


// This contract is a ERC721A contract that allows for the minting of tokens from a whitelist

// Only addresses that are on the whitelist can mint tokens
// Addresses that are on the whitelist can only mint once
// Addresses that are not on the whitelist cannot mint

contract Coolios is ERC721A, Ownable, ReentrancyGuard {

    // FIELDS
    // Base URI for the token
    string private _baseURI_;
    // Base URI for the token PLUS token ID as string for URL
    using Strings for uint256;
    // Need MerkleProof for bytes32[];
    using MerkleProof for bytes32[];
    uint256 private immutable MAX_SUPPLY = 666;
    uint256 public MAX_BATCH_MINT = 20;
    uint256 public mintRate = 0.1 ether;
    // This is our merkle root That I made in JavaScript (getMerkleRoot.js)
    bytes32 private MerkleRoot;
    // Mapping to track whether an address has already minted
    mapping(address => bool) private _hasMinted;

    // 0x0x0x0x0x0x0x0x0x0x0x0x0
    // CONSTRUCTOR
    // 0x0x0x0x0x0x0x0x0x0x0x0x0

    // merkle root determines who can mint
    constructor(string memory baseURI, bytes32 Root)
        ERC721A("Coolios", "COOL")
        Ownable(msg.sender)
        
    {
        require(Root != bytes32(0), "Empty Root");        
        MerkleRoot = Root;
        _baseURI_ = baseURI;
    }

    // 0x0x0x0x0x0x0x0x0x0x0x0x0
    // MINT FUNCITON
    // 0x0x0x0x0x0x0x0x0x0x0x0x0

    function mint(bytes32[] memory proof, uint8 quantity)

        external
        payable
        
        checkWhitelisted(proof)
        canMintOnlyOnce
        supplyCheck(quantity)
        checkCost(mintRate, quantity)
        batchMintCheck(quantity)
        nonReentrant
    {
        _hasMinted[msg.sender] = true;
        //this _mint is from ERC721A with reduces gas costs
        _mint(msg.sender, quantity);
        _sendFunds(msg.value);
    }

    // 0x0x0x0x0x0x0x0x0x0x0x0x0
    // MODIFIER FUNCTIONS
    // 0x0x0x0x0x0x0x0x0x0x0x0x0

    // Only addresses that are on the whitelist can mint tokens
    // Addresses that are on the whitelist can only mint once
    // Addresses that are not on the whitelist cannot mint

    // checks if address is on our whitelist (getMerkleRoot.js)
    modifier checkWhitelisted(bytes32[] memory proof) {
        require(proof.verify(MerkleRoot, keccak256(abi.encodePacked(msg.sender))), "Not whitelisted!!!!!");
        _;
    }

    // Modifier to check if the address has already minted
    modifier canMintOnlyOnce() {
        require(!_hasMinted[msg.sender], "Address has already minted");
        _;
    }

    // Modifier to check if the batch mint quantity does not exceed the limit
    modifier batchMintCheck(uint256 quantity) {
        require(quantity <= MAX_BATCH_MINT, "Exceeds maximum batch mint limit");
        _;
    }

    // used in the mint function to check if mint addition is less than MAX supply
    modifier supplyCheck(uint256 quantity) {
        require(_totalMinted() + quantity <= MAX_SUPPLY, "Max Mint Reached");
        _;
    }

    // checks if user sends enough funds for minting
    modifier checkCost(uint256 cost, uint256 quantity) {
        // if minting 10 units, 10 X the mint rate = total value to send 
        require(msg.value >= cost * quantity, "Not enough funds supplied");
        _;
    }

    // 0x0x0x0x0x0x0x0x0x0x0x0x0
        // PUBLIC FUNCTIONS
    // 0x0x0x0x0x0x0x0x0x0x0x0x0

    // Function to generate token URI
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "Token does not exist");
        return string(abi.encodePacked(_baseURI_, tokenId.toString()));
    }

    // 0x0x0x0x0x0x0x0x0x0x0x0x0
    // OWNER FUNCTIONS
    // 0x0x0x0x0x0x0x0x0x0x0x0x0

    // change the merkleroot. possible if you were concerned about security for the original root
    function setMerkleRoot(bytes32 root) external onlyOwner {
        require(root.length > 0, "Empty Root");
        MerkleRoot = root;
    }

   // to take out ETH from the contract to the owner address
    function withdraw() external onlyOwner nonReentrant {
        _sendFunds(address(this).balance);
    }

    // internal used in withdraw function to send funds to owner 
    function _sendFunds(uint256 _totalAmount) internal {
        payable(address(owner())).transfer(_totalAmount);
}
}

1 Answer 1

1

Solved:

I had to add a function in the main contract to return the msg.sender first. It appears the vm.prank only sets the address over in the contract and not inside the testing test. I pranked the msg.sender, then made a test variable that == the return of the helper function in the main contract that returns _msgSenderERC721A, then i use that vairiable inside the test to complete the test.

Main contract helper:

//only used for testing the msg.sender in Coolios.t.sol
function getMsgSenderFromCoolios() public view returns (address){
return _msgSenderERC721A();
}

Test file

//test for the msg.sender
function test_MintingCoolios() public{
    for (uint256 i = 0; i < whitelistAddresses.length; i++) {
   

    vm.deal(whitelistAddresses[i], 5 ether);
    vm.startPrank(whitelistAddresses[i]);
    address _testAddr = coolios.getMsgSenderFromCoolios(); 
    //assert that the address is the same as the whitelist address
    emit log_address(_testAddr);
    assertEq(_testAddr, whitelistAddresses[i]);
    coolios.mint{value: 100000000000000000 }(proofs[i], 1);
   

    vm.stopPrank();
    }

enter image description here

1

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.