0

I'm trying to buy some CHY with USDT on Uniswap main net, fails with error 'TransferHelper: TRANSFER_FROM_FAILED',

This is my failed tx link.

This is a successful tx link from someone else.

I notice my transaction fee is abnormally high, much higher than other successful txs, and no matter how I raise the gasLimit, it always used up, only takes more time.

On the page of successful tx there is Uniswap V2: CHY-USDT, which is not used in my code, is this the reason that my tx never find a correct way, but always keep finding and eat up all the gas?

How to correct the code?

Update 1: add "approve()" before transacting. The "approve()" succeeded the first time, after that it never succeeded again, always fails like this

const CHY = new Token(ChainId.MAINNET, '0x35A23BC27c345b36DeE700a256F69C6334030971', 9)
const USDT = new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6)

const pair = await Fetcher.fetchPairData(CHY, USDT)

const route = new Route([pair], USDT) // direction, from USDT -> CHY

var base = ethers.BigNumber.from(10).pow(route.path[0].decimals)
var amountIn = ethers.BigNumber.from(50).mul(base) // 50 * 10^6 == 50 USDT
console.log(amountIn.toString()) // 50 USDT

const trade = new Trade(
    route, 
    new TokenAmount(USDT, amountIn),
    TradeType.EXACT_INPUT) 


const slippageTolerance = new Percent('50', '10000') // 0.5%
const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
const path = [USDT.address, CHY.address] // <---------check direction
const to = MY_WALLET;
const deadline = Math.floor(Date.now() / 1000) + 20*60 // 20 minutes

const provider = ethers.getDefaultProvider('mainnet', {
    infura: INFURA_ID
})

const signer = new ethers.Wallet(MY_WALLET_PRIVATE_KEY)
const account = signer.connect(provider)
const uniswap = new ethers.Contract(
    '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', // fixed Route02.addr
    ['function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'],
    account
)

// approve
const contract_usdt = new ethers.Contract(
    addr_usdt,
    //['function approve(address spender, uint value) public'],
    ['function approve(address spender, uint amount) public returns(bool)'],
    account
);
const tx_approve = await contract_usdt.approve(uniswap.address, amountIn, {
    gasPrice: gasPrice,
    gasLimit: gasLimit
})

console.log('tx_approve: ' + tx_approve.hash);

const receipt_approve = await tx_approve.wait()
console.log(receipt_approve)


const tx = await uniswap.swapExactTokensForTokens(
    amountIn.toString(),
    amountOutMin.toString(),
    path,
    to,
    deadline,
    { 
        gasLimit: 400000,
        gasPrice: ethers.utils.parseUnits("40", "gwei") 
    }
)
console.log(`Transaction hash: ${tx.hash}`)
const receipt = await tx.wait()

And I tried path=[USDT, UNIWAP_V2_CHY_USDT, CHY], also fails

1 Answer 1

1

You did not approve USDT to the router address before attempting the swap.

The USDT contract uses assert instead of revert or throw to cancel the transaction because of the lack of approval. Using assert will consume all the gas left. It's bad practice by Tether to use assert, but it's probably because their contract is really old.

EDIT: Also, the USDT contract requires you to set the allowance to 0 before making another approve call. Here is the relevant code of the USDT contract:

function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {

    // To change the approve amount you first have to reduce the addresses`
    //  allowance to zero by calling `approve(_spender, 0)` if it is not
    //  already 0 to mitigate the race condition described here:
    //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));

    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
}
2
  • Code updated with approve(), it succeeded the first time and never succeed again, please refer to the updated post.
    – aj3423
    Commented Jul 16, 2021 at 20:01
  • See my edit, it's the USDT contract that is weird
    – Undead8
    Commented Jul 16, 2021 at 20:06

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.