0

I’m getting 0 allowances even after approving admin to spend, although msg.sender has a balance of 100. I tried to sleep for 10 seconds too before executing transferFrom() it fails and even after that allowance is 0. But it didn’t work.

function applyForVerification(uint _amount) public {
    require(msg.sender != admin, "admin can't apply verification");
    console.log(IERC20(token).balanceOf(msg.sender));
    bool _success = IERC20(token).approve(address(this), _amount);
    if (_success) {
        console.log(IERC20(token).allowance(msg.sender, address(this)));
        account[msg.sender].status = Status.Requested;
        emit PendingVerifications(msg.sender, account[msg.sender].status);
    } else {
        revert("failed to approve");
    }
}

remix logs

console.log:
 100
 10

Test logs The applyForVerification() in which the approval was supposed to work is the second one in the screenshot and even after 5 to 6 tests I checked allowances it's still 0. In between those tests, I also halted execution for 10 seconds.

verifyArtist()

function verifyArtist(address _address, uint _amount) public onlyAdmin {
        require(account[_address].status == Status.Pending, "invalid status for verification");
        address clone = Clones.clone(implementation);
        Implementation(clone).initialize(_address, tiers.tier1, tiers.tier2 , admin, token, _amount);
        implementationAddress[_address] = address(clone);
        account[_address].status = Status.Verified;
        console.log("Contract Address", address(clone));
        emit PendingVerifications(_address, account[_address].status);
        emit Verified(_address, address(clone));
    }

Implementation Contract Initialization

function initialize(address _address, uint _tier1, uint _tier2, address _admin, address _token, uint _amount) public initializer payable {
        address = _address;
        admin = _admin;
        token = _token;
        tiers = Tiers(_tier1, _tier2);
        console.log("Allowance", IERC20(token).allowance(_address, msg.sender));
        IERC20(token).transferFrom(_address, address(this), _amount);
    }

remix logs:

console.log:
 Allowance 10
 
transact to Factory.verifyArtist errored: VM error: revert.

revert
    The transaction has been reverted to the initial state.
Reason provided by the contract: "ERC20: transfer amount exceeds allowance".

Updates

My bad I was wrong at spender, when the clone has created the msg.sender for transferFrom() in initialize() in implementation contract will be the factory contract so I have made necessary changes to the spender. In the remix, I’m getting the allowance value(10) but still, transferFrom() reverts with exceeded allowance reason. And in hardhat, the allowance is still 0.

4
  • You need to wait for the approve transaction to be mined before calling allowance
    – Julissa DC
    Dec 7, 2021 at 16:05
  • @JulissaDC Yeah that's what I delayed the test by 10 seconds then executed it further, then to I’m getting allowances 0 Dec 7, 2021 at 16:07
  • Can you iclude that here? in the snipped you pste you call approve and allowance on the same function
    – Julissa DC
    Dec 7, 2021 at 16:08
  • @JulissaDC please check the updates Dec 7, 2021 at 16:23

2 Answers 2

0

this function will revert everytime, since youre trying to approve and spend in the same txs(no matter how much you sleep).

thats why approving and spending are two different txs when you try to use a dex like uni/sushi etc.

suggest to separate concerns regarding the approval and transfer into two distinct functions.

Further, beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729

3
  • No, approve and spend(transferFrom()) are in different functions. please refer to the test logs I added in updates. The transferFrom() is in verifyArtist() which is a completely difficult function that clones my implementation contract and implementation contract as initialize() in which the transferFrom() is being executed. Dec 7, 2021 at 16:23
  • please provide the code for the implementation in concern if you can, from what you have provided seems like the bug is probably in the code you've omitted or a more generic logic bug between your contract clones
    – 0xninja
    Dec 7, 2021 at 16:31
  • I made some changes can you check now? Dec 8, 2021 at 6:15
0

I was wrong!

  • transferFrom() is a bit different when it is called in another contract the caller of the transferForm() is the contract itself so the user needs to approve/increaseAllowance of the contract in which the transferFrom() is being called.

  • In my case, I was approving in Factory contract with address(this) as the spender so the caller of transferFrom() should be factory contract but my transferFrom() is in initializer function in implementation contract so the caller of transferFrom() should be implementation contract itself, but I thought, the clone of the implementation contract is performed in factory contract so the caller would be factory contract.

  • So according to my use case, this pattern didn’t work for me, as applyForVerification() was called before creating an implementation contract for the user so I had to perform approval/increaseAllowance off-chain after deploying the implementation contract and use this contract address as a spender. And remove transferFrom() from initializer function and add it in different function as deposit(). check here how I got the solution.

contract Factory {
    function applyForVerification(uint _amount) public {
        require(msg.sender != admin, "admin can't apply verification");
        console.log(IERC20(token).balanceOf(msg.sender));
        bool _success = IERC20(token).approve(address(this), _amount);
        if (_success) {
            console.log(IERC20(token).allowance(msg.sender, address(this)));
            account[msg.sender].status = Status.Requested;
            emit PendingVerifications(msg.sender, account[msg.sender].status);
        } else {
            revert("failed to approve");
        }
    }

    function verifyArtist(address _address, uint _amount) public onlyAdmin {
        require(account[_address].status == Status.Pending, "invalid status for verification");
        address clone = Clones.clone(implementation);
        Implementation(clone).initialize(_address, tiers.tier1, tiers.tier2 , admin, token, _amount);
        implementationAddress[_address] = address(clone);
        account[_address].status = Status.Verified;
        console.log("Contract Address", address(clone));
        emit PendingVerifications(_address, account[_address].status);
        emit Verified(_address, address(clone));
    }
}

contract Implementation {
    function initialize(address _address, uint _tier1, uint _tier2, address _admin, address _token, uint _amount) public initializer {
        address = _address;
        admin = _admin;
        token = _token;
        tiers = Tiers(_tier1, _tier2);
        console.log("Allowance", IERC20(token).allowance(_address, msg.sender));
        IERC20(token).transferFrom(_address, address(this), _amount);
    }
}

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.