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
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