I've tried approving with multiple different spender addresses but no luck.

Token approval works fine when I try a different token, e.g. DAI. I've made sure I have the correct KNC contract address and correct spender addresses. I'm not even able to approve the spender when it's something so standard as the Uniswap V2 Router contract.

I've also checked to ensure the tokenQty is less than or equal to the tokens I have in my account, and that it is being passed down as a BN.

Here are the contract addresses I'm using (all Ropsten):

KNC: '0x7B2810576aa1cce68F2B118CeF1F36467c648F92'
KYBER_NETWORK_PROXY_ADDRESS: '0xd719c34261e099Fdb33030ac8909d5788D3039C4'
KYBER_NETWORK_PROXY_ADDRESS_ALT: '0x818E6FECD516Ecc3849DAf6845e3EC868087B755'
UNISWAP_ROUTER_V2_ADDRESS: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'

Here is my token approval helper function:

const approveTokenTrade = async (
  tokenSymbol: string,
  tokenQty: number,
  addressToApprove: string,
  web3: Web3
) => {
  const accountAddress = getAccountAddress();
  const chainId = getChainId();
  const privateKey = getPrivateKey();

  const tokenInstance = new web3.eth.Contract(

  const approveTxData = tokenInstance.methods
    .approve(addressToApprove, tokenQty)

  const approveTxObject = {
    from: accountAddress,
    to: TOKEN_SYMBOL_TO_ADDRESS_MAP.get(tokenSymbol),
    data: approveTxData,
    gas: 4000000,

  const signedApproveTxObject = await web3.eth.accounts.signTransaction(

  await web3.eth.sendSignedTransaction(
    signedApproveTxObject.rawTransaction as string

Thank you!

  • If the same code works for other tokens like DAI then the problem isn't your code but the contract you are calling. Did you check possible error conditions on the token contract?
    – Ismael
    Commented Apr 30, 2021 at 4:19
  • Yes, you are spot on. Please see my response below. Thanks.
    – Sol
    Commented May 3, 2021 at 8:30

1 Answer 1


It turns out that KNC has a further condition in it's approve method. I had overlooked it assuming it'll have the standard ERC20 approve method but it does not. The further condition can be seen in:

function approve(address _spender, uint256 _value) returns (bool) {

    // 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);
    return true;

Where now we have require((_value == 0) || (allowed[msg.sender][_spender] == 0)).

This means you have to "reset" the approved value to zero if you've approved it previously for the same spender. Then you can approve the new amount.

So the approval now works fine (I can see the successful transactions).

But now I have a new problem.

The downstream trade on any of the exchange contracts (e.g. Kyber Network Proxy) fails with error: SafeERC20: low-level call failed. Similarly, I get the error 'TransferHelper: TRANSFER_FROM_FAILED' on Uniswap Router V2, which is usually due to lack of approval too.

This doesn't make sense to me as I've already successfully approved a very large amount equal to 2^255 for those spenders. I'm also sending it from the same to address, so msg.sender is the same for the approval and the trade attempt.

Does anyone know what could be going on here?


  • I'd suggest creating a new question.
    – Ismael
    Commented May 4, 2021 at 4:07

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.