Is an ERC20 transfer function enough for my use case?

I'm building a dapp where users are rewarded with a token for participation. Pretty simple eh?

User accounts are credited upon completion of a task and they can 'withdraw' however much of their balance they want to a metamask wallet. Is the contract below sufficient for this or does it need to have something else? For the record, this contract works with my dapp exactly how I want, but obviously that doesn't mean it's correct!

Thanks

contract JPEToken is ERC20 {

  //Constructor to initialise the smart contract parameters "name" and "symbol"
  constructor(string memory name, string memory symbol) ERC20(name, symbol) {
    //This is an inherited function that will mint 10,000 tokens. The math
    //equations are because of token base units
    _mint(msg.sender, 100 * (10 ** 18));
  }

  function faucet(address recipient, uint amount) external {
    _mint(recipient, amount);
  }

Emmm, so do you expect users can call this function faucet() over and over again? I think you had better set a permission for this function.

1 Like

They can call it whenever they want, but they can only receive what they have in their account!

But it seems like, even though I have 10 tokens, I can faucet 100 tokens.

The request is processed server side before the tokens are released! I'm open to all suggestions of how I could make this better as it's for a project, not a production application.

But if you has deployed this contract, I think I can call faucet() directly rather than by your server.

ah yes, excellent point, and this is exactly the kind of information I'm looking for. How can I do this (i.e. hack it) and how do I stop it? (i.e. which permissions)

For example: You can set permission for users to let them claim once every 2 hours, and claim 100 tokens per time. But this is up to you.

Thanks for your response. I've been doing some reading and apparently I also need to use the call function these days for security:

function sendViaCall(address payable _to) public payable {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        (bool sent, bytes memory data) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}

I will also implement your suggestion.

Using call will not fix the problem. The contract needs to include some way to restrict when faucet can be called.

If you have a server that will grant tokens, your server can have a private key with which it can sign "tickets" and send them to users. The users can then send this ticket to the contract to exchange it for tokens. The contrat should validate that the ticket is valid and was signed by the server.

2 Likes