0

I have a simple lottery contract. When I call functions to it from Remix IDE I get the expected result. However, when I make calls to it from React, I get nothing back (such as from manager() or getPlayers()). I am, however, able to get the contract balance. I'm not sure what I'm doing wrong here but my code is below:

Lottery.sol:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.8.0;

contract Lottery {
    address public manager;
    address[] public players;

    constructor() {
        // The manager will be the person deploying the contract
        manager = msg.sender;
    }

    modifier restrictedToManager() {
        require(
            msg.sender == manager,
            "This function is restricted to the manager"
        );
        _;
    }

    function enter() public payable {
        // Players must stake at least 0.01 ether to be entered into the lottery
        require(
            msg.value >= 0.01 ether,
            "Players must stake at least 0.01 ether to be entered into the lottery"
        );

        players.push(msg.sender);
    }

    function random() private view returns (uint256) {
        // Pseudo-random number generator
        return
            uint256(
                keccak256(
                    abi.encodePacked(block.difficulty, block.timestamp, players)
                )
            );
    }

    function pickWinner() public payable restrictedToManager {
        // Get a random player
        uint256 index = random() % players.length;

        // Pay the random player the balance on this contract
        payable(players[index]).transfer(address(this).balance);

        // Clear the array of players
        players = new address[](0);
    }

    function getPlayers() public view returns (address[] memory) {
        return players;
    }
}

web.js

import Web3 from 'web3';

const web3 = new Web3(window.ethereum);

export default web3;

lottery.js

import web3 from './web3';

const address = '0x89a7a5d894797a6f26ACA2E5f2F3b39f29bB9f96';

const abi = [
    {
        inputs: [],
        stateMutability: 'nonpayable',
        type: 'constructor',
        constant: undefined,
        payable: undefined,
        signature: 'constructor'
    },
    {
        inputs: [],
        name: 'enter',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
        constant: undefined,
        payable: true,
        signature: '0xe97dcb62'
    },
    {
        inputs: [],
        name: 'getPlayers',
        outputs: [[Object]],
        stateMutability: 'view',
        type: 'function',
        constant: true,
        payable: undefined,
        signature: '0x8b5b9ccc'
    },
    {
        inputs: [],
        name: 'manager',
        outputs: [[Object]],
        stateMutability: 'view',
        type: 'function',
        constant: true,
        payable: undefined,
        signature: '0x481c6a75'
    },
    {
        inputs: [],
        name: 'pickWinner',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
        constant: undefined,
        payable: true,
        signature: '0x5d495aea'
    },
    {
        inputs: [[Object]],
        name: 'players',
        outputs: [[Object]],
        stateMutability: 'view',
        type: 'function',
        constant: true,
        payable: undefined,
        signature: '0xf71d96cb'
    }
];

export default new web3.eth.Contract(abi, address);

App.js

import './App.css';
import web3 from './web3';
import React, { useEffect, useState } from 'react';
import lottery from './lottery';

function App() {
  const [manager, setManager] = useState('');
  const [players, setPlayers] = useState([]);
  const [balance, setBalance] = useState('');
  const [value, setValue] = useState('');
  const [loading, setLoading] = useState(false);
  const [winMessage, setWinMessage] = useState('');

  useEffect(() => {
    async function loadWeb3() {
      const manager = await lottery.methods.manager().call();

      setManager(manager);
      update();
    }

    loadWeb3();
  }, []);

  async function update() {
    const players = await lottery.methods.getPlayers().call();
    const balance = await web3.eth.getBalance(lottery.options.address);

    setPlayers(players);
    setBalance(balance);
  }

  async function onSubmit(event) {
    event.preventDefault();
    const accounts = await web3.eth.getAccounts();

    setLoading(true);
    await lottery.methods.enter().send({ from: accounts[0], value: web3.utils.toWei(value, 'ether') });
    update();
    setLoading(false);
  }

  async function onClick() {
    const accounts = await web3.eth.getAccounts();

    setLoading(true);
    await lottery.methods.pickWinner().send({ from: accounts[0] });
    update();
    setWinMessage('A winner has been picked!');
    setLoading(false);
  }

  return (
    <div className="App">
      <h2>Lottery Contract</h2>
      <p>This contract is managed by {manager}.
        There are currently {players.length} people entered,
        competing to win {web3.utils.fromWei(balance, 'ether')} ether!
      </p>
      <hr />
      {loading ? <p>Waiting for transaction to complete...</p> :
        <div>
          <form onSubmit={onSubmit}>
            <h4>Want to try your luck?</h4>
            <div>
              <label>Amount of ether to enter</label>
              <input
                value={value}
                onChange={event => setValue(event.target.value)}>
              </input>
              <button type="submit">Enter</button>
            </div>
          </form>

          <hr />

          <h4>Ready to pick a winner?</h4>
          {winMessage && <h5>{winMessage}</h5>}
          <button onClick={onClick}>Pick a winner!</button>
        </div>
      }
    </div>
  );
}

export default App;
5
  • Did you connect to metamask? docs.metamask.io/guide/… – Ismael Jan 26 at 1:29
  • Yes I'm pretty sure I am since whenever I make a transaction using my app the meta mask extension pops up for permission to send funds – Jefumaru Jan 26 at 1:47
  • Does any error appear on the browser console? – Ismael Jan 26 at 2:24
  • No errors in the browser console – Jefumaru Jan 26 at 21:39
  • Since web3 is initialized on import perhaps it is a little too early. I'd try to initialize dynamically when loadWeb3 is called. – Ismael Jan 27 at 4:30
0

I figured out that it was an issue with my abi. As you can see in my abi, outputs was listed as outputs: [[Object]]. This is because I was getting my abi from a console log output and it wasn't spitting out the entire object. To get the entire object, I had to use util.inspect. Anyways, here is my corrected lottery.js file:

Lottery.js:

import web3 from './web3';

const address = '0x89a7a5d894797a6f26ACA2E5f2F3b39f29bB9f96';

const abi = [
    {
        inputs: [], stateMutability: 'nonpayable', type: 'constructor'
    },
    {
        inputs: [],
        name: 'enter',
        outputs: [],
        stateMutability: 'payable',
        type: 'function'
    },
    {
        inputs: [],
        name: 'getPlayers',
        outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }],
        stateMutability: 'view',
        type: 'function'
    },
    {
        inputs: [],
        name: 'manager',
        outputs: [{ internalType: 'address', name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function'
    },
    {
        inputs: [],
        name: 'pickWinner',
        outputs: [],
        stateMutability: 'payable',
        type: 'function'
    },
    {
        inputs: [{
            internalType: 'uint256', name: '', type: 'uint256'
        }],
        name: 'players',
        outputs: [{ internalType: 'address', name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function'
    }
];

export default new web3.eth.Contract(abi, address);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.