diff --git a/package.json b/package.json index 73b30e3..04f7767 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "test:watch": "react-scripts test --env=jsdom" }, "dependencies": { + "@formo/analytics": "^1.25.0", "@fortawesome/free-regular-svg-icons": "^6.1.0", "@fortawesome/free-solid-svg-icons": "^6.1.0", "@fortawesome/react-fontawesome": "^0.2.0", diff --git a/src/components/Overlays/OverlayNFT.tsx b/src/components/Overlays/OverlayNFT.tsx index 842b26c..fdab2cf 100644 --- a/src/components/Overlays/OverlayNFT.tsx +++ b/src/components/Overlays/OverlayNFT.tsx @@ -5,6 +5,7 @@ import { faPaperPlane } from '@fortawesome/free-solid-svg-icons'; import {OverlaySendNFT} from './OverlaySendNft'; import { shortAddress } from '../../utils'; import { OverlayAction } from '../OverlayAction'; +import { IFormoAnalytics } from '@formo/analytics'; interface OverlayNFT { nftName?: string; @@ -20,6 +21,7 @@ interface OverlayNFT { selectedSigner:any; provider:any; isDarkMode?:boolean; + analytics_formo?: IFormoAnalytics; } export const OverlayNFT = ({ @@ -36,6 +38,7 @@ export const OverlayNFT = ({ selectedSigner, provider, isDarkMode, + analytics_formo, }: OverlayNFT): JSX.Element => { const [sendNFT, setSendNFT] = useState(false); const [isNFTLoaded, setIsNFTLoaded] = useState(false); @@ -118,6 +121,7 @@ export const OverlayNFT = ({ selectedSigner={selectedSigner} provider={provider} isDarkMode={isDarkMode} + analytics_formo={analytics_formo} /> ); diff --git a/src/components/Overlays/OverlaySend.tsx b/src/components/Overlays/OverlaySend.tsx index ed62e60..8b7df2f 100644 --- a/src/components/Overlays/OverlaySend.tsx +++ b/src/components/Overlays/OverlaySend.tsx @@ -5,6 +5,7 @@ import { OverlayAction } from '../OverlayAction'; import './overlay.css'; import { Notify, ReefSigner, Token } from '../../state'; import { Provider } from '@reef-chain/evm-provider'; +import { IFormoAnalytics } from '@formo/analytics'; interface OverlaySend { tokenAddress?: string; @@ -18,6 +19,7 @@ interface OverlaySend { isDarkMode?:boolean; isWalletConnect?:boolean; handleWalletConnectModal?:(val:boolean)=>void; + analytics_formo?: IFormoAnalytics } export const OverlaySend:React.FC = ({ @@ -32,6 +34,7 @@ export const OverlaySend:React.FC = ({ isDarkMode, isWalletConnect, handleWalletConnectModal, + analytics_formo, }: OverlaySend): JSX.Element => { return ( = ({ isWalletConnect={isWalletConnect} handleWalletConnectModal={handleWalletConnectModal} isDarkMode={isDarkMode} + analytics_formo={analytics_formo} /> )} diff --git a/src/components/Overlays/OverlaySendNft.tsx b/src/components/Overlays/OverlaySendNft.tsx index ef93de7..33d28d5 100644 --- a/src/components/Overlays/OverlaySendNft.tsx +++ b/src/components/Overlays/OverlaySendNft.tsx @@ -8,6 +8,7 @@ import { Provider, Signer } from '@reef-chain/evm-provider'; import { shortAddress } from '../../utils'; import { OverlayAction } from '../OverlayAction'; import { ReefSigner } from '../../state'; +import { IFormoAnalytics } from '@formo/analytics'; interface OverlaySendNFT { nftName?: string; @@ -22,6 +23,7 @@ interface OverlaySendNFT { selectedSigner:any; provider:any; isDarkMode?:boolean; + analytics_formo?:IFormoAnalytics, } const nftTxAbi = [ @@ -142,7 +144,8 @@ export const OverlaySendNFT = ({ accounts, selectedSigner, provider, - isDarkMode + isDarkMode, + analytics_formo, }: OverlaySendNFT): JSX.Element => { const [isAccountListOpen, setAccountsListOpen] = useState(false); const [destinationAddress, setDestinationAddress] = useState(''); @@ -168,6 +171,16 @@ export const OverlaySendNFT = ({ }; const transferNFT = async (from: string, to: string, _amount: number, nftContract: string, _signer: Signer, _provider: Provider, _nftId: string): Promise => { + if(analytics_formo){ + analytics_formo.track("nft_transfer", { + nft_contract: nftContract, + nft_id: _nftId, + from_address: from, + to_address: to, + amount: _amount, + status:"initiated" + }) + } if (!isFormValid || transactionInProgress) { return; } @@ -181,6 +194,16 @@ export const OverlaySendNFT = ({ storageLimit: 2000, }, }); + if(analytics_formo){ + analytics_formo.track("nft_transfer", { + nft_contract: nftContract, + nft_id: _nftId, + from_address: from, + to_address: toAddress, + amount: _amount, + status:"successful" + }) + } Uik.notify.success('Transaction Successful!'); clearStates(); onClose(); @@ -193,6 +216,17 @@ export const OverlaySendNFT = ({ } else { Uik.notify.danger('Unknown error occurred, please try again'); } + if(analytics_formo){ + analytics_formo.track("nft_transfer", { + nft_contract: nftContract, + nft_id: _nftId, + from_address: from, + to_address: toAddress, + amount: _amount, + error:error.toString(), + status:"failed" + }) + } } finally { setTransactionInProgress(false); } diff --git a/src/components/Overlays/OverlaySwap.tsx b/src/components/Overlays/OverlaySwap.tsx index 544be5c..9fec773 100644 --- a/src/components/Overlays/OverlaySwap.tsx +++ b/src/components/Overlays/OverlaySwap.tsx @@ -11,6 +11,7 @@ import * as store from '../../store'; import { onSwap as hooksOnswap, useSwapState } from '../../hooks'; import { OverlayAction } from '../OverlayAction'; import { Finalizing, Trade } from '../PoolActions'; +import { IFormoAnalytics } from '@formo/analytics'; const REEF_ADDRESS = '0x0000000000000000000000000000000001000000'; const MAX_SLIPPAGE = 20; @@ -28,6 +29,7 @@ interface OverlaySwap { network:libNet.DexProtocolv2 |undefined; notify:(message: string, type?: Notify) => void; isDarkMode?:boolean; + analytics_formo?: IFormoAnalytics; } const poolWithReservesToPool = (p: PoolWithReserves): Pool => ({ @@ -67,7 +69,8 @@ export const OverlaySwap = ({ pools, network, notify, - isDarkMode=false + isDarkMode=false, + analytics_formo, }: OverlaySwap): JSX.Element => { const [address1, setAddress1] = useState(tokenAddress); const [address2, setAddress2] = useState('0x'); @@ -162,6 +165,7 @@ export const OverlaySwap = ({ setFinalized(true); if (onClose) onClose(); }, + analytics_formo, }); const onSwitch = (): void => { tradeDispatch(store.switchTokensAction()); diff --git a/src/components/Transfer/Send.tsx b/src/components/Transfer/Send.tsx index d84d952..db2357a 100644 --- a/src/components/Transfer/Send.tsx +++ b/src/components/Transfer/Send.tsx @@ -36,6 +36,7 @@ import SendPopup from "../PoolActions/ConfirmPopups/Send"; import { DownIcon } from "../common/Icons"; import { retrieveReefCoingeckoPrice } from "../../api"; import UsdAmountField from "../PoolActions/UsdAmountField"; +import { IFormoAnalytics, TransactionStatus } from "@formo/analytics"; interface Send { tokens: Token[]; @@ -47,6 +48,7 @@ interface Send { isDarkMode?:boolean; isWalletConnect?:boolean; handleWalletConnectModal?:(val:boolean)=>void; + analytics_formo?: IFormoAnalytics; } const getSignerEvmAddress = async ( @@ -194,7 +196,8 @@ export const Send = ({ tokenAddress, isWalletConnect, isDarkMode, - handleWalletConnectModal + handleWalletConnectModal, + analytics_formo }: Send): JSX.Element => { const [to, setTo] = useState(""); const [status, setStatus] = useState("Send"); @@ -266,6 +269,11 @@ export const Send = ({ const onSend = async (): Promise => { try { + if(analytics_formo){analytics_formo.transaction({ + address:signer.address, + chainId:13939, + status:TransactionStatus.STARTED + })} setLoading(true); if(isWalletConnect && handleWalletConnectModal)handleWalletConnectModal(true); ensureTokenAmount(token); @@ -275,7 +283,7 @@ export const Send = ({ if (isNativeTransfer(token)) { setStatus("Transfering native REEF"); const nativeAddr = await getSignerNativeAddress(to, provider); - await nativeTransfer(amount, nativeAddr, provider, signer); + await nativeTransfer(amount, nativeAddr, provider, signer,analytics_formo!); } else { setStatus("Extracting evm address"); const toAddress = isNativeAddress(to) @@ -283,6 +291,15 @@ export const Send = ({ : to; setStatus(`Transfering ${token.symbol}`); await tokenContract.transfer(toAddress, amount); + if(analytics_formo){ + analytics_formo.transaction({ + status:TransactionStatus.CONFIRMED, + address:signer.evmAddress!, + chainId:13939, + to:toAddress, + value:amount, + }) + } } Uik.notify.success({ diff --git a/src/components/Transfer/TransferComponent.tsx b/src/components/Transfer/TransferComponent.tsx index 9834261..02e2728 100644 --- a/src/components/Transfer/TransferComponent.tsx +++ b/src/components/Transfer/TransferComponent.tsx @@ -44,6 +44,7 @@ import { AccountListModal } from '../AccountSelector/AccountListModal'; import { ConfirmLabel } from '../common/Label'; import { Button } from '../common/Button'; import { TxStatusHandler, TxStatusUpdate } from '../../utils/transactionUtil'; +import { IFormoAnalytics, TransactionStatus } from '@formo/analytics'; interface TransferComponent { tokens: Token[]; @@ -53,6 +54,7 @@ interface TransferComponent { onTxUpdate?: TxStatusHandler; accounts: ReefSigner[]; currentAccount: ReefSigner; + analytics_formo?: IFormoAnalytics; } const TX_IDENT_ANY = 'TX_HASH_ANY'; @@ -145,6 +147,7 @@ export const TransferComponent = ({ onTxUpdate, currentAccount, accounts, + analytics_formo }: TransferComponent): JSX.Element => { const [availableTxAccounts, setAvailableTxAccounts] = useState( [], @@ -273,6 +276,13 @@ export const TransferComponent = ({ return; } try { + if(analytics_formo){ + analytics_formo.track('transfer_initiated', { + token_address: txToken.address, + token_symbol: txToken.symbol, + amount: txToken.amount, + }); + } setIsLoading(true); ensureTokenAmount(txToken); if (utils.isAddress(to)) { @@ -284,6 +294,20 @@ export const TransferComponent = ({ ); setLastTxIdentInProgress(txIdent); getUpdateTxCallback([onTxUpdate, setTxUpdateData])({ txIdent }); + if(analytics_formo){ + analytics_formo.track('transfer_sent_to_evm', { + token_address: txToken.address, + token_symbol: txToken.symbol, + amount: txToken.amount, + tx_ident: txIdent, + }); + analytics_formo.transaction({ + status:TransactionStatus.CONFIRMED, + address:from.evmAddress!, + chainId:13939, + to:to, + }) + } } else if (isSubstrateAddress(to)) { const txIdent = sendToNativeAddress( provider, @@ -294,6 +318,14 @@ export const TransferComponent = ({ ); setLastTxIdentInProgress(txIdent); getUpdateTxCallback([onTxUpdate, setTxUpdateData])({ txIdent }); + if(analytics_formo){ + analytics_formo.track('transfer_sent_to_substrate', { + token_address: txToken.address, + token_symbol: txToken.symbol, + amount: txToken.amount, + tx_ident: txIdent, + }); + } } } catch (err) { console.log('onSendTxConfirmed error =', err); @@ -306,6 +338,14 @@ export const TransferComponent = ({ }, addresses: [from.address], }); + if(analytics_formo){ + analytics_formo.track('transfer_error', { + token_address: txToken?.address, + token_symbol: txToken?.symbol, + amount: txToken?.amount, + error_message: err.message || err, + }); + } } setIsLoading(false); }; diff --git a/src/hooks/useSwapState.ts b/src/hooks/useSwapState.ts index 519c838..bfb3ad2 100644 --- a/src/hooks/useSwapState.ts +++ b/src/hooks/useSwapState.ts @@ -33,9 +33,40 @@ import { } from '../utils'; import { findToken } from './useKeepTokenUpdated'; import { useLoadPool } from './useLoadPool'; +import { IFormoAnalytics, TransactionStatus } from '@formo/analytics'; type Network = network.DexProtocolv2; +const CHAIN_ID = 13939; + +// Helper to build common analytics_formo payload +const buildBasePayload = ( + token1: TokenWithAmount, + token2: TokenWithAmount, + account: ReefSigner, + settings: { percentage: number; deadline: number }, + batchTxs?: boolean, +) => ({ + sellToken: { + address: token1.address, + symbol: token1.symbol, + amount: token1.amount, + price: token1.price, + }, + buyToken: { + address: token2.address, + symbol: token2.symbol, + amount: token2.amount, + price: token2.price, + }, + userAddress: account.evmAddress, + slippagePercentage: settings.percentage, + deadline: settings.deadline, + chainId: CHAIN_ID, + flowType: batchTxs ? 'batch' : 'sequential', + timestamp: Date.now(), +}); + const swapStatus = ( sell: TokenWithAmount, buy: TokenWithAmount, @@ -66,17 +97,6 @@ const swapStatus = ( ensure(reserved1.gt(0) || reserved2.gt(0), 'Insufficient amounts'); - // WIP checking for ReefswapV2: K error - // Temporary solution was with `swapExactTokensForTokensSupportingFeeOnTransferTokens` function! - // Error still arrives when using `swapExactTokensForTokens` - - // const balanceAdjuster1 = token1.balance.mul(1000).sub(amountIn1.mul(3)); - // const balanceAdjuster2 = token2.balance.mul(1000).sub(amountIn2.mul(3)); - - // const reserved = reserved1.mul(reserved2).mul(1000 ** 2); - // const balance = balanceAdjuster1.mul(balanceAdjuster2); - // ensure(balance.gte(reserved), 'Deliquified pool'); - // ensure(amountOut1.eq(amountIn1) && amountOut2.eq(amountIn2), 'Deliquified pool') return { isValid: true, text: 'Trade' }; } catch (e) { return { isValid: false, text: e.message }; @@ -95,6 +115,7 @@ interface UseSwapState { waitForPool?: boolean; pool?: Pool; } + export const useSwapState = ({ state, tokens, @@ -123,10 +144,11 @@ export const useSwapState = ({ let loadedPool: Pool | undefined; let isPoolLoading = false; if (waitForPool) { + // Avoid querying pool based on token addresses and receive pool from props loadedPool = poolProp; } else { - // eslint-disable-next-line react-hooks/rules-of-hooks + // eslint-disable-next-line react-hooks/rules-of-hooks [loadedPool, isPoolLoading] = useLoadPool( sell, buy, @@ -135,13 +157,13 @@ export const useSwapState = ({ isLoading, ); } + useEffect(() => { if (loadedPool) { dispatch(setPoolAction(loadedPool)); } }, [loadedPool]); - // Updating swap tokens useEffect(() => { const foundToken1 = findToken(address2, tokens); if (prevAddress2.current !== address2 || (!tokenBuySet.current && buy.address) @@ -163,14 +185,12 @@ export const useSwapState = ({ } }, [tokens, tokenPrices, address1, address2]); - // Updating token prices useEffect(() => { if ((tokenPrices[address1] || tokenPrices[address2]) && (tokenBuySet.current || tokenSellSet.current)) { dispatch(setTokenPricesAction(tokenPrices)); } }, [tokenPrices]); - // Updating swap state useEffect(() => { let [currentStatus, currentIsValid, currentIsLoading] = [ '', @@ -207,6 +227,7 @@ interface OnSwap { updateTokenState: () => Promise; onSuccess?: (...args: any[]) => any; onFinalized?: (...args: any[]) => any; + analytics_formo?: IFormoAnalytics; } export const onSwap = ({ @@ -218,15 +239,22 @@ export const onSwap = ({ updateTokenState, onSuccess, onFinalized, + analytics_formo, }: OnSwap) => async (): Promise => { const { token1, settings, token2, isValid, isLoading, } = state; + if (!isValid || isLoading || !account || !network) { return; } + const { signer, address, evmAddress } = account; const { percentage, deadline } = resolveSettings(settings); + const basePayload = buildBasePayload(token1, token2, account, { percentage, deadline }, batchTxs); + + // Track swap initiated + analytics_formo?.track('swap_initiated', basePayload); try { dispatch(setLoadingAction(true)); @@ -235,14 +263,17 @@ export const onSwap = ({ const sellAmount = calculateAmount(token1); const minBuyAmount = calculateAmountWithPercentage(token2, percentage); const reefswapRouter = getReefswapRouter(network.routerAddress, signer); - const sellTokenContract = new Contract(token1.address, ERC20, signer); dispatch(setStatusAction('Executing trade')); + + // Prepare approval transaction const approveTransaction = await sellTokenContract.populateTransaction.approve( network.routerAddress, sellAmount, ); + + // Prepare trade transaction const tradeTransaction = await reefswapRouter.populateTransaction.swapExactTokensForTokensSupportingFeeOnTransferTokens( sellAmount, minBuyAmount, @@ -262,21 +293,44 @@ export const onSwap = ({ ); if (batchTxs) { + // === BATCHED TRANSACTION FLOW === + + // Track batch approval started + analytics_formo?.track('batch_approval_started', { + ...basePayload, + routerAddress: network.routerAddress, + approvalAmount: sellAmount.toString(), + }); + const tradeExtrinsic = signer.provider.api.tx.evm.call( tradeTransaction.to, tradeTransaction.data, toBN(tradeTransaction.value || 0), - toBN(582938 * 2), // hardcoded gas estimation, multiply by 2 as a safety margin - toBN(64 * 2), // hardcoded storage estimation, multiply by 2 as a safety margin + toBN(582938 * 2),// hardcoded gas estimation, multiply by 2 as a safety margin + toBN(64 * 2),// hardcoded storage estimation, multiply by 2 as a safety margin ); - // Batching extrinsics + // Track batch trade started + analytics_formo?.track('batch_trade_started', { + ...basePayload, + sellAmount: sellAmount.toString(), + minBuyAmount: minBuyAmount.toString(), + path: [token1.address, token2.address], + }); + const batch = signer.provider.api.tx.utility.batchAll([ approveExtrinsic, tradeExtrinsic, ]); // Signing and awaiting when data comes in block + analytics_formo?.track('batch_signed_and_sent', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.BROADCASTED, + address: evmAddress!, + chainId: CHAIN_ID, + }); + const signAndSend = new Promise((resolve, reject): void => { batch.signAndSend( address, @@ -284,18 +338,44 @@ export const onSwap = ({ (status: any) => { const err = captureError(status.events); if (err) { + // Track batch error + analytics_formo?.track('batch_error', { + ...basePayload, + stage: 'sign_and_send', + errorMessage: err, + }); reject({ message: err }); } if (status.dispatchError) { + // Track batch error + analytics_formo?.track('batch_error', { + ...basePayload, + stage: 'dispatch', + errorMessage: status.dispatchError.toString(), + }); reject({ message: status.dispatchError.toString() }); } if (status.status.isInBlock) { + // Track batch in block + analytics_formo?.track('batch_in_block', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.BROADCASTED, + address: evmAddress!, + chainId: CHAIN_ID, + }); resolve(); } // If you want to await until block is finalized use below if if (status.status.isFinalized) { + // Track batch finalized + analytics_formo?.track('batch_finalized', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.CONFIRMED, + address: evmAddress!, + chainId: CHAIN_ID, + }); + if (onFinalized) onFinalized(); - Uik.notify.success({ message: 'Blocks have been finalized', aliveFor: 10, @@ -305,8 +385,25 @@ export const onSwap = ({ ); }); await signAndSend; + } else { - // Approve + // === SEQUENTIAL TRANSACTION FLOW === + + // Track approval started + analytics_formo?.track('approval_started', { + ...basePayload, + routerAddress: network.routerAddress, + approvalAmount: sellAmount.toString(), + }); + + // Track approval signed and sent + analytics_formo?.track('approval_signed_and_sent', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.BROADCASTED, + address: evmAddress!, + chainId: CHAIN_ID, + }); + const signAndSendApprove = new Promise((resolve, reject) => { approveExtrinsic.signAndSend( address, @@ -314,13 +411,32 @@ export const onSwap = ({ (status: any) => { const err = captureError(status.events); if (err) { + // Track approval error + analytics_formo?.track('approval_error', { + ...basePayload, + stage: 'sign_and_send', + errorMessage: err, + }); reject({ message: err }); } if (status.dispatchError) { + // Track approval error + analytics_formo?.track('approval_error', { + ...basePayload, + stage: 'dispatch', + errorMessage: status.dispatchError.toString(), + }); console.error(status.dispatchError.toString()); reject({ message: status.dispatchError.toString() }); } if (status.status.isInBlock) { + // Track approval in block + analytics_formo?.track('approval_in_block', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.CONFIRMED, + address: evmAddress!, + chainId: CHAIN_ID, + }); resolve(); } }, @@ -328,7 +444,14 @@ export const onSwap = ({ }); await signAndSendApprove; - // Swap + // Track trade started + analytics_formo?.track('trade_started', { + ...basePayload, + sellAmount: sellAmount.toString(), + minBuyAmount: minBuyAmount.toString(), + path: [token1.address, token2.address], + }); + const tradeResources = await signer.provider.estimateResources(tradeTransaction); const tradeExtrinsic = signer.provider.api.tx.evm.call( tradeTransaction.to, @@ -338,6 +461,14 @@ export const onSwap = ({ tradeResources.storage.lt(0) ? toBN(0) : toBN(tradeResources.storage), ); + // Track trade signed and sent + analytics_formo?.track('trade_signed_and_sent', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.BROADCASTED, + address: evmAddress!, + chainId: CHAIN_ID, + }); + const signAndSendTrade = new Promise((resolve, reject) => { tradeExtrinsic.signAndSend( address, @@ -345,19 +476,44 @@ export const onSwap = ({ (status: any) => { const err = captureError(status.events); if (err) { + // Track trade error + analytics_formo?.track('trade_error', { + ...basePayload, + stage: 'sign_and_send', + errorMessage: err, + }); reject({ message: err }); } if (status.dispatchError) { + // Track trade error + analytics_formo?.track('trade_error', { + ...basePayload, + stage: 'dispatch', + errorMessage: status.dispatchError.toString(), + }); console.error(status.dispatchError.toString()); reject({ message: status.dispatchError.toString() }); } if (status.status.isInBlock) { + // Track trade in block + analytics_formo?.track('trade_in_block', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.BROADCASTED, + address: evmAddress!, + chainId: CHAIN_ID, + }); resolve(); } - // If you want to await until block is finalized use below if if (status.status.isFinalized) { + // Track trade finalized + analytics_formo?.track('trade_finalized', basePayload); + analytics_formo?.transaction({ + status: TransactionStatus.CONFIRMED, + address: evmAddress!, + chainId: CHAIN_ID, + }); + if (onFinalized) onFinalized(); - Uik.notify.success({ message: 'Blocks have been finalized', aliveFor: 10, @@ -369,6 +525,12 @@ export const onSwap = ({ await signAndSendTrade; } + // Track swap completed + analytics_formo?.track('swap_completed', { + ...basePayload, + success: true, + }); + if (onSuccess) onSuccess(); Uik.notify.success({ @@ -378,6 +540,12 @@ export const onSwap = ({ Uik.dropConfetti(); } catch (error) { + // Track swap failed (catch-all for any unhandled errors) + analytics_formo?.track('swap_failed', { + ...basePayload, + errorMessage: errorHandler(error.message), + }); + const message = errorHandler(error.message); Uik.notify.danger({ message: `An error occurred while trying to complete your trade: ${message}`, diff --git a/src/utils/transactionUtil.ts b/src/utils/transactionUtil.ts index 0f507b5..d67b48b 100644 --- a/src/utils/transactionUtil.ts +++ b/src/utils/transactionUtil.ts @@ -4,6 +4,7 @@ import { Provider } from "@reef-chain/evm-provider"; import { BigNumber } from "ethers"; import {network as nw} from "@reef-chain/util-lib"; import { ReefSigner } from "../state"; +import { IFormoAnalytics, TransactionStatus } from "@formo/analytics"; export type TxStatusHandler = (status: TxStatusUpdate) => void; @@ -70,7 +71,8 @@ export const nativeTransfer = async ( amount: string, destinationAddress: string, provider: Provider, - signer: ReefSigner + signer: ReefSigner, + analytics_formo?:IFormoAnalytics, ): Promise => { const transfer = provider.api.tx.balances.transfer( destinationAddress, @@ -82,9 +84,23 @@ export const nativeTransfer = async ( { signer: signer.signer.signingKey }, (status) => { if (status.dispatchError) { + if(analytics_formo){ + analytics_formo.transaction({ + status:TransactionStatus.REJECTED, + address:signer.evmAddress!, + chainId:13939, + }) + } reject({ message: status.dispatchError.toString() }); } if (status.status.isInBlock) { + if(analytics_formo){ + analytics_formo.transaction({ + status:TransactionStatus.BROADCASTED, + address:signer.evmAddress!, + chainId:13939, + }) + } resolve(); } } diff --git a/yarn.lock b/yarn.lock index f580025..360fd58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1780,6 +1780,15 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c" integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ== +"@formo/analytics@^1.25.0": + version "1.25.0" + resolved "https://registry.yarnpkg.com/@formo/analytics/-/analytics-1.25.0.tgz#20ffc8ee7a27aaa0e6f03c26c70fcfee594a4627" + integrity sha512-T58Dvpg17I7xUk6TYrBm//xk/c/Y5kkSpZMQ+kdhE33rj34xkKkbU4nPEklLGkGCwJKGVLamTAvf4mT4E9WbEQ== + dependencies: + ethereum-cryptography "3.2.0" + fetch-retry "6.0.0" + mipd "0.0.7" + "@fortawesome/fontawesome-common-types@6.7.2": version "6.7.2" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz#7123d74b0c1e726794aed1184795dbce12186470" @@ -2364,6 +2373,13 @@ dependencies: "@noble/hashes" "1.7.0" +"@noble/curves@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.0.tgz#13e0ca8be4a0ce66c113693a94514e5599f40cfc" + integrity sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg== + dependencies: + "@noble/hashes" "1.8.0" + "@noble/curves@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.1.tgz#9654a0bc6c13420ae252ddcf975eaf0f58f0a35c" @@ -7910,6 +7926,17 @@ ethereum-blockies-base64@^1.0.2: dependencies: pnglib "0.0.1" +ethereum-cryptography@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-3.2.0.tgz#42a04b57834bf536e552b50a70b9ee5057c71dc6" + integrity sha512-Urr5YVsalH+Jo0sYkTkv1MyI9bLYZwW8BENZCeE1QYaTHETEYx0Nv/SVsWkSqpYrzweg6d8KMY1wTjH/1m/BIg== + dependencies: + "@noble/ciphers" "1.3.0" + "@noble/curves" "1.9.0" + "@noble/hashes" "1.8.0" + "@scure/bip32" "1.7.0" + "@scure/bip39" "1.6.0" + ethereum-cryptography@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" @@ -8176,6 +8203,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: node-domexception "^1.0.0" web-streams-polyfill "^3.0.3" +fetch-retry@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-6.0.0.tgz#4ffdf92c834d72ae819e42a4ee2a63f1e9454426" + integrity sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag== + figures@^1.0.1: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -11339,6 +11371,11 @@ minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1. resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +mipd@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mipd/-/mipd-0.0.7.tgz#bb5559e21fa18dc3d9fe1c08902ef14b7ce32fd9" + integrity sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg== + mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"