2

Recently we were trying to create an application that allows the user to buy an NFT using an ERC-20 token (let's say for these purposes it's a stablecoin like DAI or USDC). So from figuring everything out we

  public async approve(): Promise<void> {
    const ticketPrice = 100;

    // Approve the contract to transfer on your behalf
    const contract = new this.web3js.eth.Contract(TestUSDContract.abi, this.USD_ADDRESS);
    const success = await contract.methods.approve('<CONTRACT ADDRESS>', ticketPrice).send({from: '<USER ADDRESS>'});
    console.log('Status ' + success);

    //Then purchase the item the user wants
    const contract2 = new this.web3js.eth.Contract(LotteryContract.abi, this.CONTRACT_ADDRESS);
    const value = await contract2.methods.buyItem().send({from: '<USER ADDRESS>'});
    console.log('contract2 ' + value);
  }

For transparency, the following is the contract method used for testing

  function buyTicket() external returns (string memory) {
    testUsdContract.transferFrom(msg.sender, address(this), itemAmount);
    return "SUCCESS";
  }

This all works, however, from a UX perspective, the user gets two dialogs from the UI which seems like bad UX. Also from what I understand (and makes sense) the approve method MUST be called from the user's account (and cannot be from the contract as msg.sender on the ERC-20 token will be our contract).

  1. I was wondering what workarounds exists for this AND how other apps accomplish accept ERC-20 items as payment (in a user-friendly manner preferably).

  2. This also begs the question, what happens when the first request is successful and the second fails (could be of network error, or a contract exception), how would one handle that usually in a DAPP (and how have other dapps done this?)

1

There is no way around having to do two transactions with ERC-20 tokens for approve and transferFrom currently. You cannot send tokens to a contract directly, because the receiving contract is not aware of the transaction (like it would be with a regular ETH transaction).

Most dApps do an approval up front with the maximum possible uint256 value, which essentially means that the dApp can transfer an unlimited number of tokens. This way you don't have to do an approval for every transaction, only once to onboard the user. The downside of this however is that your contract now controls the full token balance of the user, which is not great from a security perspective.

There are some EIPs that try to solve this problem, e.g.:

  • EIP-2711, which introduces new transaction types, including a transaction type to do batch transactions. You could do the approval and transfer in a single batch transaction. If you're interested in learning more about transaction types, I wrote a blog post about those and EIP-2711, which you can find here.
  • EIP-3074, which allows contracts to send transactions in the context of an EOA. This way, a contract could do a call using the AUTHCALL opcode, and msg.sender would be the address of the user, rather than the contract address. I made a contract which can do arbitrary (batch) calls, which you can find here.

Unfortunately until one of these (or another similar EIP) is included on the Ethereum network, there's no way around having to send two transactions

This also begs the question, what happens when the first request is successful and the second fails (could be of network error, or a contract exception), how would one handle that usually in a DAPP (and how have other dapps done this?)

You can check beforehand if the user already did the approval (by checking if the allowance is high enough on the ERC-20 contract). This way it doesn't matter if the second transaction fails.

3
  • Excellent answer! A couple of follow-up questions I have (pardon my ignorance in advance). You have already implemented 3074, does that mean this is already possible on the blockchain network? What exactly do you mean when "one if these EIPs needs to be included on the Eth network", the Eth miners need to upgrade their nodes to the latest version?
    – MilindaD
    May 16 at 14:59
  • Do you have any references / examples to DApps I could take a look at that do bulk approval for a large amount? (preferably from a front end perspective)
    – MilindaD
    May 16 at 15:00
  • EIP-3074 is currently only available on the Puxi testnet, not on the mainnet. It may be included in the next hard fork on Ethereum (which should happen in July). You can look at something like Uniswap or Compound for an example for the UI.
    – Morten
    May 16 at 15:05

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.