0

I have contract method transferFrom that failed when I calling it the site's from backend, but in the same time it works from Remix.

Both calls are performed through Metamask. Initially I thought that it could be related to gas amount but when I called method from Remix and specified gas amount that was used for failed transaction - it works, so I have no idea what is the problem.

Here is failed transaction that called using this code:

Eth.contract._approve(toAddress, fromAddress, amount)
.then(async result => {
  const tx = await Eth.contract.transferFrom.sendTransaction(fromAddress, toAddress, amount);

  return res.json({success: true, data: {from: fromAddress, to: toAddress, amount: amount, tx: tx}});
})
.catch(error => {
  return res.boom.badImplementation('Unable to approve transfer ' + amount + ' tokens to the address ' + fromAddress, {success: false});
});

_approve is special function in the contract that accessible only for contract owner and has 3 parameters insteand of 2 required by ERC20 interface.

And here is working transaction that is called from Remix with the same contract loaded.

2

Finally I got it. Issue is unrelated to the contract code except couple of require statements inside it.

Methods transferFrom relies on internal approved member that contains alloed amount to transfer between 2 accounts. It was done to merge duplicated functionality of methods transfer and transferFrom (see ERC20 Token interface). But it was mistake.

On the site's backend called consequently 2 methods using await:

  • approve
  • transferFrom

But when approve was already awaited and response received it wasn't actually mined by the network and approved mapping does not contains required amount. Thus statement require(approved[_from][_to] >= _amount) failed.

Solution: do not merge logically unmergeble.

1

Using transferFrom involves three accounts:

  • owner: Account who has the tokens
  • recipient: Account that will receive tokens
  • sender: Account making the transaction

Now the sequence should be

  1. the owner has to approve the sender

    token.approve(sender, amount, { from: owner });
    
  2. The sender can make the transfer

    token.transferFrom(owner, recipient, amount, { from: sender });
    
  • This is exactly what I did, just omitted approve step. Approved correct amount, as I mentioned this call works when performed through Remix. – Alex G.P. Nov 16 '17 at 2:01
  • @AlexG.P. In your code it doesn't have the transaction options ie the last parameter { from: sender }, without that you are sending the transaction from the default account. – Ismael Nov 16 '17 at 2:19
  • it does not works. By default I have only one wallet/address as all transactions sender - it is contract owner/creator. I updated post with full approve-transfer code. Approve and transfer performed by contract creator, so it is the same as you specified. And it does not helps. – Alex G.P. Nov 16 '17 at 5:13
  • Moreover, as you can see the same transaction with the same transaction initiator works perfectly from remix. – Alex G.P. Nov 16 '17 at 5:14
  • 1
    Both transactions are identical, in geth vm trace they have a similar start, make two external calls, the failed tx makes a revert around step 1050 but the successful keep going until step 2190. Without the contract code is impossible to say anything more. – Ismael Nov 16 '17 at 5:58

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.