UNPKG

resmic

Version:

Decentralise payment infrastructure. Integrate crypto payment facilities without any hassle!

301 lines (283 loc) 11.6 kB
import { Chains, ERC20_ABI, TokenAddress, Tokens } from "../Constant/Constant"; import { connectWalletFn, getProvider, getSigner, switchNetwork } from "./ConnectMeta"; import { ethers } from "ethers"; import BigNumber from "bignumber.js"; import {ToastContainer, toast } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; import { getCurrentTokenPrice } from "../Constant/LivePrice"; /** * * Main Function to handle all the EVM transaction. * @param {String} Blockchain // EVM Blockchain name * @param {String} Token // Token name * @param {String} Address_ // Wallet address to send funds. * @param {INT} Amount // Amount in USD * @param {INT} PaymentConfirmations // Wait for No. of blocks confirmations. Default 2 * @returns {Bool} Payment Status */ export async function makeEVMPayment (Blockchain, Token, Address_, Amount, PaymentConfirmations=2) { let Address = Address_.EVM; let paymentStatus = false; // Connect Metamask Wallet, Switching network if necessary. let connectWallet = await connectWalletFn(); let connectdUserAddress = connectWallet?.account let connectedchainId = connectWallet?.chainId // console.log("connectedchainId", connectedchainId) let requiredChainID = Chains[Blockchain].id; if(connectedchainId !== requiredChainID) { // Switching the networ if the users is on another network on Metamask. await switchNetwork(requiredChainID) } /* 3 types of transactions i. Native tokens ii. Stable tokens iii. Unstable tokens. */ let tokenType = Tokens[Token] if(tokenType?.id === requiredChainID) { // Handling Native tokens let tokenSymbol = Tokens[Token]?.dname let nativePayment = await nativeTokenPayment(Amount, Address, tokenSymbol, PaymentConfirmations) paymentStatus = nativePayment; return nativePayment } else if(tokenType?.type === "stable") { // Handling Stable tokens let tokenAddress = TokenAddress tokenAddress = tokenAddress[Blockchain][Token] let stableTx = await requestERC20Payment(Amount, tokenAddress, connectdUserAddress, Address, PaymentConfirmations ) paymentStatus = stableTx return stableTx; } else if (tokenType?.type === "unstable") { // Handling UnStable tokens let tokenSymbol = Tokens[Token]?.dname; let tokenAddress = TokenAddress tokenAddress = tokenAddress[Blockchain][Token] // Calculate the amount here. let livePrice = await getCurrentTokenPrice(tokenSymbol); let amount = Amount / livePrice; amount = parseFloat(amount) let ERC20tx = await requestERC20Payment(amount, tokenAddress, connectdUserAddress, Address, PaymentConfirmations ) paymentStatus = ERC20tx return ERC20tx; } return paymentStatus; } /** * Function for Native token transfers. * @returns {Bool} Payment Status */ const nativeTokenPayment = async (_amount, _address,_tokenSymbol, _paymentConfirmations) => { const signer = await getSigner(); let amount = _amount; let liveTokenPrice = await getCurrentTokenPrice(_tokenSymbol)// Live token price let decimal = 18 let tokenAmount = amount / liveTokenPrice tokenAmount = BigNumber(Math.floor((parseFloat(tokenAmount) * (10 ** decimal)))) tokenAmount = tokenAmount.toFixed() try { // Transfer of funds to address const tx = await signer.sendTransaction({ to: _address, // Funds will be received by the address value: tokenAmount, // gasLimit: '0x5028', // maxPriorityFeePerGas: '0x3b9aca00', // maxFeePerGas: '0x2540be400', }); await tx.wait(); let paymentUpdate = await checkBlockConformationsNative(tx.hash, tokenAmount, _paymentConfirmations, _tokenSymbol); return paymentUpdate; } catch (error) { toast.error('Something went wrong.', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); console.log(error); return false; } }; /** * Function ckecks the block conformation of the payment on the blockchain for ERC20 token only. * The user will wait until the the transaction is confirmed until specified blocks. * @param {String} tx // Transaction hash after the payment. * @param {INT} _amount * @param {INT} _noOfBlockConformation * @param {String} _tokenSymbol * @returns {Bool} returns the paymetn update. */ const checkBlockConformationsNative = async (tx, _amount, _noOfBlockConformation, _tokenSymbol) => { let provider = await getProvider(); const confirmationsRequired = _noOfBlockConformation; const receipt = await provider.waitForTransaction( tx, confirmationsRequired ); if (receipt.status === 1) { let actualTokenTransfer = await checkTokenTransfersNative(tx);// It retunes the actual tokens received at the blockchain. if (actualTokenTransfer !== 0) { if (actualTokenTransfer >= _amount) { // setPaymentStatus(true); // setIsPaymentCompleted(true); toast.success('Payment done successfully:)', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return true; } else { // alert("Something went wrong"); toast.error('Something went wrong', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return false; } } else { toast.error('Unable to process payment\n Please try again ', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return false; } } else { toast.error('Transaction failed to be processed', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return false; } }; /** * Verifies the amount to token actually transfereed. Native tokens * @param {String} tx * @returns {String} String '0' or 'Amount_Of_Tokens' */ const checkTokenTransfersNative = async (tx) => { let provider = await getProvider(); try { // Get the transaction details const transaction = await provider.getTransaction(tx); if (transaction && transaction.confirmations > 0) { let amountInWei = transaction.value; amountInWei = BigNumber(amountInWei.toString()) return amountInWei.toFixed(); } else { // console.log("Transaction not found or not confirmed yet."); toast.error('Transaction not found', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return "0"; } } catch (error) { console.log("Err", error); toast.error('Unable to get transaction details', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return "0"; } }; /** * If user is selected with non-native token, function request the payment. * */ const requestERC20Payment = async (_amount, _tokenAddress, _userAddress, _toAddress, _paymentConfirmations) => { // setIsLoading(true); const signer = await getSigner(); try { /** * The ERC20 payment requires 2 transactions * 1. Approval of token * 2. Transafer of token */ const contractInstance = new ethers.Contract( _tokenAddress, ERC20_ABI, signer ); let decimals = await contractInstance.decimals(); decimals = decimals.toString(); let amount = _amount // amount = BigNumber((parseFloat(amount) * 10 ** decimals)) // Updates in the amount. amount = BigNumber(Math.floor((parseFloat(amount) * (10 ** decimals))) ) amount = amount.toFixed(); const getApprove = await contractInstance.approve( _userAddress, amount ); await getApprove.wait(); toast.success('Token approved', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); let tx = await contractInstance.transferFrom( _userAddress, _toAddress, amount, { gasLimit: 100000 } ); await tx.wait(); let tx2 = await checkBlockConformations(tx.hash, amount, _paymentConfirmations, _tokenAddress ); return tx2 } catch (error) { toast.error('Something went wrong.', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); console.log(error); } }; /** * Function ckecks the block conformation of the payment on the blockchain for ERC20 token only. * The user will wait until the the transaction is confirmed until specified blocks. * @param {String} tx // Transaction hash after the payment. * @param {INT} _amount * @param {INT} _noOfBlockConformation * @param {String} _tokenAddress * @returns {Bool} returns the paymetn update. */ const checkBlockConformations = async (tx, _amount, _noOfBlockConformation, _tokenAddress) => { let provider = await getProvider(); const confirmationsRequired = _noOfBlockConformation; const receipt = await provider.waitForTransaction( tx, confirmationsRequired ); // Checks the status of the transaction if (receipt.status === 1) { let actualTokenTransfer = await checkTokenTransfers(tx, _tokenAddress); if (actualTokenTransfer !== "0") { if (_amount >= actualTokenTransfer) { // setPaymentStatus(true); toast.success('Payment done successfully.', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); // console.log("Payment done successfully") return true; } else { toast.error('Something went wrong.', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); console.log("Not sufficient amount transferred: "); return false; } } else { toast.error('Unable to process payment\n Please try again', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return false; } } else { toast.error('Transaction failed to be processed', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return false; } }; /** * Verifies the amount to token actually transfereed. ERC20 tokens * @param {String} transactionHash * @param {String} _tokenAddress * @returns {String} String '0' or 'Amount_Of_Tokens' */ const checkTokenTransfers = async (transactionHash, _tokenAddress) => { let provider = await getProvider(); try { const transactionReceipt = await provider.getTransactionReceipt( transactionHash ); if (transactionReceipt && transactionReceipt.status === 1) { const tokenContract = new ethers.Contract( _tokenAddress, ERC20_ABI, provider ); const filter = tokenContract.filters.Transfer(null, null, null); const events = await tokenContract.queryFilter( filter, transactionReceipt.blockNumber, transactionReceipt.blockNumber ); const event = events.find( (event) => event.transactionHash === transactionHash ); if (event) { const amount = event.args.value.toString(); return amount; } else { toast.error('No token transfer event found for the tx.', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return "0"; } } else { toast.error('Transaction not found or not successful.', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return "0"; } } catch (error) { console.error("Error reading transaction details:", error); toast.error('No transaction found!', { position: toast.POSITION.TOP_CENTER,theme: "dark"}); return "0"; } };