0

Please see attached Tenderly stack trace:

https://dashboard.tenderly.co/shared/simulation/86e27f67-6f7e-4122-83da-d43f455fb57a

I'm not sure why transferfrom is failing even through I have granted uint256 unlimited allowance to the contract withdrawing my USDT.

Contract code (Vyper)

@external
def swap(
    _from: address,
    _to: address,
    _amount: uint256,
    _minout: uint256,
    _order_id: Bytes[10],
    _tofiat: bool,
    _totopup: bool,
) -> uint256:

    sanctioned: bool = Chainanalysis(0x40C57923924B5c5c5455c48D93317139ADDaC8fb).isSanctioned(msg.sender)
    assert sanctioned != True, "sanctioned"
    assert self.paused == False, "paused"

    if (_totopup):
        assert _to == USDC_eth, "USDC"
    
    if (_tofiat):
        assert _to == USDC_eth, "USDC"

    
    ERC20(_from).transferFrom(msg.sender,self,_amount)
    ERC20(_from).approve(UNISWAP, _amount)
    balance_before: uint256 = ERC20(_to).balanceOf(self)
    result: Bytes[32] = raw_call(
        UNISWAP,
        concat(
            method_id("exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))"),
            convert(_from, bytes32),
            convert(_to, bytes32),
            convert(3000, bytes32), #pool fee
            convert(self, bytes32), #recipient
            convert(block.timestamp, bytes32), #deadline
            convert(_amount, bytes32),
            convert(_minout, bytes32),
            convert(0, bytes32), #sqrtPriceLimitX96
        ),
        max_outsize=32
    )

    output: uint256 = convert(result, uint256)
    balance_after: uint256 = ERC20(_to).balanceOf(self)
    assert balance_after - balance_before == output, "not balanced"
    assert output >= _minout, "too little"
    if (_totopup):
        fees: uint256 = output * self.fee_topup/10000
        ERC20(_to).transfer(profits,fees)
        log TopedUp(msg.sender,_amount,output,_order_id)
        return output
    elif(_tofiat):
        fees: uint256 = output * self.fee_fiat/10000
        ERC20(_to).transfer(profits,fees)
        if (self.topartner):
            ERC20(USDC_eth).transfer(self.partner_wallet,_amount-fees)
        log Success(msg.sender,_amount,output,_order_id)
        return output
    else:
        fees: uint256 = output * self.fee_swap/10000
        if (_to == USDC_eth):
            ERC20(_to).transfer(profits,fees)
            ERC20(_to).transfer(msg.sender,output-fees)
        else:
            ERC20(_to).transfer(msg.sender,output-fees)
            new_output: uint256 = self.to_usdc(_to,fees)
            ERC20(USDC_eth).transfer(profits,new_output)
        log Success(msg.sender,_amount,output,_order_id)
        return output

05/11/2023 updated:

Based on feedback provided by @Vas Trek have updated this to check if allowance is already set, if it is, to zero allowance and to reset it.

Issue still persists.

allowance: uint256 = ERC20(_from).allowance(self,UNISWAP) 
if (allowance == 0): 
ERC20(_from).approve(UNISWAP, _amount) 
else: 
ERC20(_from).approve(UNISWAP, 0) ERC20(_from).approve(UNISWAP,_amount) 

Kind regards,

2
  • can you post the code of the contract? It seems that the transfer actually worked and it failed after that
    – Vas Treck
    Nov 2, 2023 at 0:56
  • @VasTreck I have updated my question to include the source code of the contract method being called. Grateful for your assistance Nov 3, 2023 at 12:55

1 Answer 1

0

I think it's failing on ERC20(_from).approve(UNISWAP, _amount)

two possible reasons:

a) UNISWAP address is zero

b) (most probable) UNISWAP was already approved to take USDTs from this contract.

The second option is USDT token specific. USDT's approve function includes this statement: require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));

It means that if the approval was already granted and you want to reset it, you must run approve(address, 0) before making new approve call.

1
  • Dear @vastreck, it appears as though the issue still persists despite implementing the code changes you suggested (please see updated answer above). Any thoughts? Nov 5, 2023 at 21:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.