Can't withdrawal ERC-20 tokens from faucet contract

Hey all,
I recently published an ERC-20 token on mainnet and all is working well. Now I am attempting to write a Faucet smart contract in order to distribute coins to whoever asks for them. Note that this wouldn't be a faucet that mints new tokens directly from the Token contract but one that I can preload with tokens (via sending to the faucet from my wallet).

I've put together a smart contract for the faucet from various sources and everything works fine except for the 'withdraw' function. I am getting the following error:

truffle(development)> FaucetDeployed.withdraw()
Uncaught Error: Returned error: VM Exception while processing transaction: revert
    at evalmachine.<anonymous>:0:16
    at sigintHandlersWrap (vm.js:273:12)
    at Script.runInContext (vm.js:142:14)
    at runScript (C:\Users\Jonathan\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\console.js:364:1)
    at Console.interpret (C:\Users\Jonathan\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\console.js:379:1)
    at bound (domain.js:421:15)
    at REPLServer.runBound [as eval] (domain.js:432:12)
    at REPLServer.onLine (repl.js:909:10)
    at REPLServer.emit (events.js:400:28)
    at REPLServer.emit (domain.js:475:12)
    at REPLServer.Interface._onLine (readline.js:434:10)
    at REPLServer.Interface._normalWrite (readline.js:588:12)
    at Socket.ondata (readline.js:246:10)
    at Socket.emit (events.js:400:28) {
  data: {
    '0xf08ce8522dce0fb4c19ac791e5a7960055e1ad0e106761efa8f411c1cc9e23c9': { error: 'revert', program_counter: 677, return: '0x' },
    stack: 'c: VM Exception while processing transaction: revert\n' +
      '    at Function.c.fromResults (C:\\Users\\Jonathan\\AppData\\Roaming\\npm\\node_modules\\ganache-cli\\build\\ganache-core.node.cli.js:4:192416)\n' +
      '    at w.processBlock (C:\\Users\\Jonathan\\AppData\\Roaming\\npm\\node_modules\\ganache-cli\\build\\ganache-core.node.cli.js:42:50915)\n' +
      '    at runMicrotasks (<anonymous>)\n' +
      '    at processTicksAndRejections (internal/process/task_queues.js:95:5)',
    name: 'c'

Here's my Faucet smart contract code:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract Faucet {
    address payable owner;
    IERC20 private _token;
    uint256 public withdrawalAmount = 50 * (10 ** 18);

    event Withdrawal(address indexed to, uint amount);
    event Deposit(address indexed from, uint amount);

    constructor (IERC20 token) {
        _token = token;
        owner = payable(msg.sender);
    }

    // Accept any incoming amount
    receive() external payable {
        emit Deposit(msg.sender, msg.value);
    }

    // Give out ether to anyone who asks
    function withdraw() public {
        // Limit withdrawal amount to 1,000 tokens
        require(
            withdrawalAmount <= 1000 * (10 ** 18),
            "Request exceeds maximum withdrawal amount of 1000 ICHC"
          );

        require(
            _token.balanceOf(address(this)) >= withdrawalAmount,
            "Insufficient balance in faucet for withdrawal request"
        );

        require(
            msg.sender != address(0),
            "Request must not originate from a zero account"
        );

        // Send the amount to the address that requested it
        _token.transfer(msg.sender, withdrawalAmount);
    }

    // setter for withdrawl amount
    function setWithdrawalAmount(uint256 amount) public onlyOwner {
        // Limit max withdrawal amount to 10,000 tokens
        require(amount <= 10000 * (10 ** 18));
        withdrawalAmount = amount * (10 ** 18);
    }

    // Contract destructor
    function destroy() public onlyOwner {
        selfdestruct(owner);
    }

    // Access control modifier
    modifier onlyOwner {
        require(msg.sender == owner, "Only the contract owner can call this function");
        _;
    }
}

I'm deploying it as well as the ERC20 token to a local ganache blockchain via 'truffle migrate'. Then I test it using the following commands in the truffle console:

Setup:

Faucet.deployed().then(i=>{FaucetDeployed = i});
MyToken.deployed().then(s=>{ token = s });

Load Faucet with tokens:

token.transfer("0x55b9bCF39F78ef22E452d54957366cCBFffaF85E","300000000000000000000")

Check Faucet balance:

token.balanceOf("0x55b9bCF39F78ef22E452d54957366cCBFffaF85E").then((b)=> { balf = b })
balf.toString() // shows "300000000000000000000"

Attempt to withdrawal from Faucet:

FaucetDeployed.withdraw()

This results in the error at the top of this post. I have tried removing all the require statements, but same result. Can anyone spot what I'm doing wrong? I appreciate any advice - thanks!

  • Jonathan

Hi, so when users try to the function withdraw(), what is the situation you are expecting?
The contract has token, so transfer tokens from contract to user, or the contract does not have token, so transfer tokens from a wallet address to user?

Hey - the idea is that I would first send tokens from my wallet to the Faucet contract in order to 'preload' it. Then when users invoke 'withdraw()' the Faucet smart contract would disperse tokens from it's own address (ie the ones I originally sent to it) to the user.

I just deployed a contract, and send some token to the contract, then call withdraw(), everything worked well.

Kovan Transaction Hash (Txhash) Details | Etherscan

And maybe you can have a look at the Truffle documentation: Truffle | Interacting with Your Contracts