Search code examples
javascriptsoliditymetamask

Issue with metamask when sending money to the smart contract


I am new to solidity and smart contracts and I am bit confused with sending money from metamask to my smart contract. Everything seems to work fine in remix but when I use metamask I get a revert error message. I am using ganache as my local blockchain

My smart contract looks like this: -

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.10;

   import "@openzeppelin/contracts/access/Ownable.sol";


    contract Bet is Ownable{

     
     uint public Money;  // set variable to display balance on contract
     bool Odd;  // variable to set true or false
     uint TotalDiceNumber = 12;  // dice numbers from 1 - 12
     uint result;  // variable to hold the dice roll
     mapping(address => uint)  allowance;  //allowance for non owners of the contract
     uint  BetValue;  //variable for original bet value
     uint  _amount; //variable to hold value of allowance 
     bool  Active; // is the game active
     
     

     
    constructor() {
    

    }

    

    function isOwner() internal view returns(bool) {
        return owner() == msg.sender;  //returns the address who deployed it
    }
    event BetMade(address player, uint Bet);  //placeholder to send data to front end
     function receiveMoney() public payable  {  // get money(bet) from metamask and put on smart
        emit BetMade(msg.sender, BetValue); // send player and value of bet
        //Money += BetValue;  //added after react
        Money += msg.value;
        BetValue = msg.value;
            if (owner() == msg.sender) {
                Active = false;  // owner doesnt active contract to protect funds
            } else {
                Active = true;  // player activates contract when bet is placed
                require(BetValue <= 20 ether, "Bets must be below 21 Ether"); //player can only 
          bet under 20 ether/ owner can put as much money as they want on
         }
      }
    event BalanceofContract(uint Balcontract);
       function getBalance()public returns(uint) { 
        uint Bal;
        Bal = address(this).balance;
        emit BalanceofContract(Bal);
        return Bal;
        
        
    }
    
    event PlayerSelectOddEven(address player, bool OddEven);
    function selectOddorEven(bool OddEven) public {
        emit PlayerSelectOddEven(msg.sender, OddEven);
        require(msg.sender != owner(), "You are the house and cannot bet");  // house cannot bet
        require(Active = true, "Game not in play. Please place a bet"); // game must be active
        Odd = OddEven;  // create field to enter true for odd or false for Even
        //default is false

    }

    
    function Random() internal view returns(uint) {
        return uint(keccak256(abi.encodePacked
        (block.difficulty, 
        block.timestamp, 
        TotalDiceNumber)));  // create a random number from blockchain.
        //in production would look at chainlink VR but looks like there is a cost so
        //went with a less secure option
    } 
    event RollDiceOutput(address player, uint DiceNumber);
    function Rolldice() public returns(uint){
        emit RollDiceOutput(msg.sender, result);
        uint Outcome;
        require(Active = true, "Game not in play. Please place a bet");  //game must be actvie
        Outcome = Random() % TotalDiceNumber;
        result = Outcome + 1;  // use + 1 to remove 0 out of random sequence
        return result;  // function to create number
        
        }
    function OutcomeDice() public view returns (uint) {
        uint Outcome1;
        Outcome1 = result;
        return Outcome1;
        // function to view number from above function
        }
        
    function numberOddorEven() public view returns (bool) {
        bool OddorEven;  // true for odd false for even
        uint _result;
        _result = result;
            if (_result == 1  ||  _result == 3  || _result == 5 || _result == 7 || _result == 9 || 
          _result == 11) {
                OddorEven = true; 
            } else {
                OddorEven = false;
            }
            return OddorEven;
        
            }
    event Winning(address Player, uint WinningsValue);
    function addAllowance() public   {
        emit Winning(msg.sender, _amount);  
        require(msg.sender != owner(), "You are the house and cannot bet");
        require(Active = true, "Game not in play. Please place a bet");
        address _who = msg.sender; // assign player to variable
        _amount = BetValue * 2; //assign allowance twice as much original bet
        allowance[_who] == _amount;  // set allowance in mapping against player
        
        }

    event Won(address Player, uint Winnings);               
    function WinMoney() public  {  
        emit Won(msg.sender, _amount);
        bool UserInputOdd = numberOddorEven();
        bool decisionOdd = Odd;
        address _who = msg.sender; // assign player to variable
        require(Active = true, "Game not in play. Please place a bet"); 
        require(_amount > 0, "Add the house's money by adding a allowance to collect winning");
            if (UserInputOdd == decisionOdd) {
                address payable to = payable(msg.sender);  //pay the player's address
                to.transfer(_amount); // transfer the winning
                _amount = 0; //zeroed the variable for multiplying the bet
                allowance[_who] = 0; // zeroed the allowance
                BetValue = 0; // zeroed the bet
                Odd = false; // resets the odd/even input
                Active = false;  //disable the game 

            } else {
                _amount = 0;  //zeroed the variable for multiplying the bet
                allowance[_who] = 0;  // zeroed the allowance
                BetValue = 0;  // zeroed the bet
                Odd = false;  // resets the odd/even input
                Active = false;  //disable the game 

                }
            
                                    
         }

        receive() external payable {
        receiveMoney();
      }
   }

On the front end I am trying to add funds to the smart contract: -

import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import { useState } from "react"  //import library for line 9
import { ethers } from "ethers" // import library for ethers
import { abi_file } from "../constants/abi"; // import abi from different file

export default function Home() {
  const [isConnected, setIsConnected] = useState(false);  //to show a button is not connected
  const [signer, setSigner] = useState();

  async function connect() {  //create function to connect metameask
      
    if (typeof window.ethereum !== "undefined") { // check to see if metamask is installed
      try {
        await ethereum.request({ method: "eth_requestAccounts"});
        setIsConnected(true);  // set variable as true
        const connectedProvider = new ethers.providers.Web3Provider(window.ethereum);
        setSigner(connectedProvider.getSigner());
        } catch (e) {
          console.log(e);  // catch and log and errors in console(F12 in chrome)
        }
      } else {
      setIsConnected(false);
      }

}

async function Rolldice() {  //execute function

  if (typeof window.ethereum !== "undefined") { // check to see if metamask is installed

    const contractAddress = "0x89f6D41f87054127066d4639e3Ada3DeEefE5EB7";  // address of the contract
    const abi = abi_file;
    

    const contract = new ethers.Contract(contractAddress, abi, signer);  // calls the contract from the 3 variables
    
   
    try {
     // await contract.Rolldice();  //function will are calling on the sol contract
     const transactionResponse = await contract.Rolldice();
     const transactionReceipt = await transactionResponse.wait();
     var player = (transactionReceipt.events[0].args.player);
     var result = (transactionReceipt.events[0].args.DiceNumber.toString());
      //alert("Dice Number pressed");
      //var x = document.createElement("H3");
      //var t = document.createTextNode("Dice rolled from " + player);
     // x.appendChild(t);
     // document.body.appendChild(x);
      var z = document.createElement("H3");
      var w = document.createTextNode("Dice number is " + result);
      z.appendChild(w);
      document.body.appendChild(z);
           
      
    } catch (error) {
      console.log(error);
    }
  } else {
    document.getElementById("executeButton").innerHTML = 
    "Please install MetaMask";
  }
}

async function receiveMoney() {  //execute function

  if (typeof window.ethereum !== "undefined") { // check to see if metamask is installed

    const contractAddress = "0x89f6D41f87054127066d4639e3Ada3DeEefE5EB7";  // address of the contract
    const abi = abi_file;
    

    const contract = new ethers.Contract(contractAddress, abi, signer);  // calls the contract from the 3 variables
    
   
    try {
     // await contract.Rolldice();  //function will are calling on the sol contract
     const transactionResponse = await contract.receiveMoney();
     const transactionReceipt = await transactionResponse.wait();
      var Balance = (transactionReceipt.events[0].args.Bet.toString());
      //alert("Dice Number pressed");
      //var x = document.createElement("H3");
      //var t = document.createTextNode("Dice rolled from " + player);
     // x.appendChild(t);
     // document.body.appendChild(x);
      var z = document.createElement("H2");
      var w = document.createTextNode("The Balance of the contract is " + Balance);
      z.appendChild(w);
      document.body.appendChild(z);
           
      
    } catch (error) {
      console.log(error);
    }
  } else {
    document.getElementById("executeButton").innerHTML = 
    "Please install MetaMask";
  }
}



  return <div className={styles.container}>


  {isConnected ? 
  <>
  <h2>"Connected!" </h2>

  <p> Please send money to 0x89f6D41f87054127066d4639e3Ada3DeEefE5EB7 but no higher than 20 ethers</p>
  
  <button onClick= {() => receiveMoney()}>Bet Money</button>
  <br></br>
  <br></br>
  <button onClick= {() => Rolldice()}>Roll dice</button>
  <br></br>
  <br></br>
  
  </>
  : (<button onClick={() =>connect()}>Connect</button>) }

   
    </div>;


   }

Can anybody suggest where I am going wrong? Many thanks


Solution

  • First in your solidity can you emit the event after assigned the value for the variable betvalue Second you need to specify for wich account the signer is like this

        let provider = new ethers.providers.Web3Provider(window.ethereum); 
        const accounts = await provider.listAccounts(); 
        let account = null;
     if (accounts.length > 0) 
        { account = accounts[0] }
         let signer = provider.getSigner(account); 
    

    Third you need to pass the value the user send into the smart contract through argument options

    const options = { value: ethers.utils.parseEther(value) } 
    
    var transactionResponse = await contract.receiveMoney(options); 
    

    Value need to ve string like this value="1" This mean you will send 1 ethers