Search code examples
node.jssoliditysmartcontractsganache

Smart Contract could transfer ether to an address, but the balance of that address does not update


I am trying to get my smart contract to transfer all its balance to another address. The transfer line doesn't throw any errors but the balance of contract does not change afterwards.

I am using web3js with ganache to test this function:

My contract:

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.12;

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

    constructor() {
        manager = payable(msg.sender);
    }

    function enterLottery() public payable {
        require(msg.value >= .01 ether);
        players.push(payable(msg.sender));
    }

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

    function random() public view returns (uint256) {
        return
            uint256(
                keccak256(
                    abi.encodePacked(block.difficulty, block.timestamp, players)
                )
            );
    }

    function pickWinner() public { // this transfer contract balance to the account
        uint256 index = random() % players.length;
        players[index].transfer(address(this).balance);
    }
}

My test case:

beforeEach(async () => {
    accounts = await web3.eth.getAccounts();
    contract = await new web3.eth.Contract(abi)
        .deploy({ data: evm.bytecode.object })
        .send({ from: accounts[0], gas: "1000000" })
})

describe("Lottery", () => {
    it("Contract has an address? ", () => {
        assert.ok(contract.options.address)
    })

    it("Prize pool can receive ether", async () => { 
        await contract.methods.enterLottery().send({ from: accounts[1], gas: "1000000", value: "10000000000000000" });
        const contractBalance = await web3.eth.getBalance(contract.options.address)

        const hasContractReceivedEntry = contractBalance === "10000000000000000";
        assert.equal(hasContractReceivedEntry, true)
    })

    it("Winner can receive the prize pool", async () => {
        await contract.methods.enterLottery().send({ from: accounts[1], gas: "1000000", value: "10000000000000000" });
        await contract.methods.pickWinner().call();

        const contractBalance = await web3.eth.getBalance(contract.options.address)

        console.log(contractBalance) // the contract balance should be 0 after the pickWinner call, but it is still 10000000000000000 wei the enterLottery function gave
    })
})

Edit: It is confirmed that the smart contract can run enterLottery and random() as intended


Solution

  • await contract.methods.pickWinner().call();
    

    On this line, you're invoking a read-only call that doesn't update the contract state. You need to send a transaction using the .send() function - just like on the previous line.