How can I allow ERC20 token payment inside javascript function

Viewed 238

I'm working on a smart contract that allows users to pay for monthly subscriptions like Netflix, Amazon, etc... with ERC20 tokens.

Everything works well when I tested it. but when I implemented the pay function inside the front-end, so users can interact with the smart contract. the problem that I've faced is that when I clicked on a button to fire off the pay function, I had Metamask asking to confirm the approval, then once the approval is confirmed, I had to confirm the transfer of tokens. **I had to confirm the transfer of tokens. then I got another transaction to confirm the equivalent amount of tokens in ETH.

I made a lot of research but I got stuck, on can I allow the pay function to transfer tokens between two addresses only once. am I missing something?

PS: blockchain.account & blockchain.smartContract are imported from redux object state.

solidity function

function pay(uint planId) external {
    Subscription storage subscription = subscriptions[subscriber][planId];
    Plan storage plan = plans[planId];
    IERC20 token = IERC20(plan.token);
    require(
      block.timestamp > subscription.nextPayment,
      'not due yet'
    );

    token.transferFrom(subscriber, plan.merchant, plan.amount);  
    emit PaymentSent(
      msg.sender,
      plan.merchant, 
      plan.amount, 
      planId, 
      block.timestamp
    );
    subscription.nextPayment = subscription.nextPayment + plan.frequency;
  }

React function

async function pay() {
    showAlert(true, "Happy to see you, Your payment is processing...!");
    const data = await blockchain.smartContract.methods.subscriptions(Id).call();
    let monthlyPayment = String(data.monthlyPayment);
    let tokenAddress = data.amount;

    // instance of ERC20 contract
    let currency = new web3.eth.Contract(tokenIbi, tokenAddress);
    
    currency.methods.approve("0x1b4eAe2DC7Ca0b68643A26177bfC9c069B3D6E04",
                              amount).send({from: blockchain.account})
    .then(
      await currency.methods.transfer("0x1b4eAe2DC7Ca0b68643A26177bfC9c069B3D6E04",
                              amount).send({from:blockchain.account})
    )
    blockchain.smartContract.methods.pay(Id).send({from: blockchain.account})
    .once("error", (err)=> {
      console.log(err);
      showAlert(true, "Something went wrong...!");
    })
    .then((receipt)=> {
      console.log(receipt);
      showAlert(true, "Congratulations, You monthly payment has been submitted successfully");
      dispatch(fetchData(blockchain.account));
    })
  }

Pay button

<button className="btn"
                        id="launchApp-btn"
                        onClick={(e)=> {
                        e.preventDefault();
                        pay();
                    }}>
1 Answers

You are making two transaction on each monthly purchase

  1. Approve the allowance of erc20 tokens
  2. Transfer of erc20 token

Each one has its own gas cost. You may multiply the allowance of erc20 on user's first purchase (Which is not fair and brings the security issue) or continue doing the same scenario Approve&Transfer

Related