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();
}}>