0

I am having issues with a specific functionality on this smart contract. The contract allows for the purchasing of courses to be done without issue. However, when the admin account attempts to activate/deactivate the purchased courses, then MetaMask opens with the following error:

This transaction is expected to fail. Trying to execute it is expected to be expensive but fail, and is not recommended.

Also the gas fees are crazy high as shown here:

Total
0.12771752ETH
Amount + gas fee
Max amount:
0.12771752ETH

I have ran truffle test on both of these functions and they pass without issue. Please help me understand what is going on and how to remedy the problem. The entire contract is shown below:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;


contract CourseMarketplace {
 
    enum State {
        Purchased,
        Activated,
        Deactivated
    }
 
    struct Course {
        uint id; 
        uint price; 
        bytes32 proof; 
        address owner; 
        State state; 
    }
 
    mapping(bytes32 => Course) private ownedCourses; 
 
    mapping(uint => bytes32) private ownedCourseHash; 
 
    uint private totalOwnedCourses;
 
    address payable private owner;
 
    constructor(){
        setContractOwner(msg.sender);
    }
 
    /// Course has already been purchased!
    error CourseHasOwner();
 
    /// Sender is not course owner!
    error SenderIsNotCourseOwner();
 
    /// Only owner has an access!
    error OnlyOwner();

    /// Course has invalid state!
    error InvalidState();

    /// Course is not created!
    error CourseIsNotCreated();
 
    modifier onlyOwner() {
        if(msg.sender != getContractOwner()){
            revert OnlyOwner();
        }
        _;
    }
 
    function purchaseCourse(bytes16 courseId, bytes32 proof) external payable {
        bytes32 courseHash = keccak256(abi.encodePacked(courseId, msg.sender)); 

        if(hasCourseOwnership(courseHash)){
            revert CourseHasOwner();
        }
 
        uint id = totalOwnedCourses++;
 
        ownedCourseHash[id] = courseHash; 
 
        ownedCourses[courseHash] = Course({
            id: id,
            price: msg.value,
            proof: proof,
            owner: msg.sender,
            state: State.Purchased
        });
    }

    function repurchaseCourse(bytes32 courseHash) external payable {
        if (!isCourseCreated(courseHash)){
            revert CourseIsNotCreated();
        }

        if (!hasCourseOwnership(courseHash)){
            revert SenderIsNotCourseOwner();
        }

        Course storage course = ownedCourses[courseHash];

        if (course.state != State.Deactivated){
            revert InvalidState();
        }

        course.state = State.Purchased;
        
        course.price = msg.value;

    }

    function activateCourse(bytes32 courseHash) external onlyOwner {
        if (!isCourseCreated(courseHash)){
            revert CourseIsNotCreated();
        }
     
        Course storage course = ownedCourses[courseHash];

        
        if(course.state != State.Purchased){
            revert InvalidState();
        }
        
        course.state = State.Activated;
    }

    function deactivateCourse(bytes32 courseHash) external onlyOwner {
        if (!isCourseCreated(courseHash)){
            revert CourseIsNotCreated();
        }

        Course storage course = ownedCourses[courseHash];


        if(course.state != State.Purchased){
            revert InvalidState();
        }

        //Transfers ether back to owner
        (bool success, ) = course.owner.call{value: course.price}("");
        require(success, "Transfer failed!");

        course.state = State.Deactivated;
        course.price = 0;
    }
 
    function transferOwnership(address newOwner) external onlyOwner {
        setContractOwner(newOwner);
    }
 
    function getCourseCount() external view returns(uint){
        return totalOwnedCourses;
    }
 
    
    function getCourseHashAtIndex(uint index) external view returns(bytes32){
        return ownedCourseHash[index];
    }
 
    
    function getCourseByHash(bytes32 courseHash) external view returns(Course memory){
        return ownedCourses[courseHash];
    }
 
    function getContractOwner() public view returns(address) {
        return owner;
    }
 
    function setContractOwner(address newOwner) private {
        owner = payable(newOwner);       
    }

    // this is a helper function for activateCourse function
    function isCourseCreated(bytes32 courseHash) private view returns (bool) {

        return ownedCourses[courseHash].owner != 0x0000000000000000000000000000000000000000;
    }
 
    
    function hasCourseOwnership(bytes32 courseHash) private view returns(bool) {
        return ownedCourses[courseHash].owner == msg.sender;
    }
}

Also this is how I am calling it in the front end - I am using NextJS:

 const changeCourseState = async (courseHash, method) => {
    try {
      //[method] = "activateCourse" or "deactivateCourse"
      await contract.methods[method](courseHash).send({
        from: account.data
      })
    } catch (error) {
      console.error(error.message);
    }
  }

  const activateCourse = async (courseHash) => {
    changeCourseState(courseHash, "activateCourse")
  }

  const deactivateCourse = async (courseHash) => {
    changeCourseState(courseHash, "deactivateCourse")
  }

Many thanks for any and all help.

-Bodi

8
  • Will these links help you: 1, 2, 3, 4. I'm wondering if the problem be the size of your solidity code. Feb 4, 2022 at 20:57
  • Hello, thank you for the reply. Unfortunately none of these links present any way to solve my issue. I am still trying to figure out a way forward if anyone can offer advice. Many thanks! Feb 5, 2022 at 19:03
  • In case you haven't seen, hope these links help you find out the solution: 1, 2, 3. Maybe a simplification in activateCourse/deactivateCourse functions and emmiting events help you through. Feb 6, 2022 at 5:12
  • In case you've been pushing the "I will try anyway" button, it might be helpful to include the failed transaction hash in your Q, so that professionals could dive deeper into your problem and help you edditing these two functions of your contract or investigate the ownership and state mappings subjects. Feb 6, 2022 at 5:17
  • Hi RoliJ, you have been very helpful, thank you very much for so much of your time. I do appreciate it. Please post your Twitter, I'd like to follow you. I was going to do what you suggested, posting up the error message, however for whatever reason it works perfectly today lol. I changed nothing.. I don't know. I will leave this question here in hopes someone can explain why the occurrence happened/resolved. I am sure it will happen to others. Anyway, thanks again Roli! I hope you have a lovely day. -Bodi Feb 6, 2022 at 15:35

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.