diff --git a/apps/marginfi-v2-ui/package.json b/apps/marginfi-v2-ui/package.json index 4d87b05df8..20f209473d 100644 --- a/apps/marginfi-v2-ui/package.json +++ b/apps/marginfi-v2-ui/package.json @@ -64,6 +64,7 @@ "minidenticons": "^4.2.0", "next": "13.4.19", "next-pwa": "^5.6.0", + "posthog-js": "^1.93.3", "react": "18.2.0", "react-cookie": "^6.1.1", "react-copy-to-clipboard": "^5.1.0", diff --git a/apps/marginfi-v2-ui/src/components/common/Staking/StakingCard/StakingCard.tsx b/apps/marginfi-v2-ui/src/components/common/Staking/StakingCard/StakingCard.tsx index fe7335fc86..176020c286 100644 --- a/apps/marginfi-v2-ui/src/components/common/Staking/StakingCard/StakingCard.tsx +++ b/apps/marginfi-v2-ui/src/components/common/Staking/StakingCard/StakingCard.tsx @@ -34,6 +34,7 @@ import { } from "@solana/web3.js"; import { useConnection } from "~/hooks/useConnection"; import { SwapMode, useJupiter } from "@jup-ag/react-hook"; +import posthog from "posthog-js"; import JSBI from "jsbi"; import { StakeData, usePrevious } from "~/utils"; import { createJupiterApiClient } from "@jup-ag/api"; @@ -367,6 +368,9 @@ export const StakingCard: FC = () => { multiStepToast.setFailed(errorMsg); } finally { await Promise.all([refresh(), fetchLstState()]); + posthog.capture("user_stake", { + amount: depositAmountUi, + }); setDepositOption((currentDepositOption) => currentDepositOption.type === "stake" ? DEFAULT_DEPOSIT_OPTION : { ...currentDepositOption, amount: new BN(0) } ); diff --git a/apps/marginfi-v2-ui/src/pages/_app.tsx b/apps/marginfi-v2-ui/src/pages/_app.tsx index 29353a6fbf..18ec4596a4 100644 --- a/apps/marginfi-v2-ui/src/pages/_app.tsx +++ b/apps/marginfi-v2-ui/src/pages/_app.tsx @@ -10,6 +10,7 @@ import { WalletModalProvider } from "@solana/wallet-adapter-react-ui"; import { init, push } from "@socialgouv/matomo-next"; import { ToastContainer } from "react-toastify"; import { Analytics } from "@vercel/analytics/react"; +import posthog from "posthog-js"; import config from "~/config"; import { WALLET_ADAPTERS } from "~/config/wallets"; @@ -78,6 +79,7 @@ const MyApp = ({ Component, pageProps }: AppProps) => { React.useEffect(() => { setReady(true); + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { api_host: "https://app.posthog.com" }); }, []); // if account set in query param then store inn local storage and remove from url diff --git a/apps/marginfi-v2-ui/src/pages/api/user/login.ts b/apps/marginfi-v2-ui/src/pages/api/user/login.ts index 994077956a..5fc0897da3 100644 --- a/apps/marginfi-v2-ui/src/pages/api/user/login.ts +++ b/apps/marginfi-v2-ui/src/pages/api/user/login.ts @@ -16,6 +16,7 @@ import { STATUS_OK, firebaseApi, } from "@mrgnlabs/marginfi-v2-ui-state"; +import posthog from "posthog-js"; initFirebaseIfNeeded(); @@ -56,6 +57,13 @@ export default async function handler(req: NextApiRequest, res: an return res.status(STATUS_NOT_FOUND).json({ error: "User not found" }); } else { await logLoginAttempt(walletAddress, userResult.uid, "", true); + posthog.capture("user_login", { + publicKey: walletAddress, + uuid: userResult.uid, + }); + posthog.identify(userResult.uid, { + publicKey: walletAddress, + }); } } catch (error: any) { Sentry.captureException(error); diff --git a/apps/marginfi-v2-ui/src/pages/api/user/signup.ts b/apps/marginfi-v2-ui/src/pages/api/user/signup.ts index 02bb0b83c3..c5cc202d43 100644 --- a/apps/marginfi-v2-ui/src/pages/api/user/signup.ts +++ b/apps/marginfi-v2-ui/src/pages/api/user/signup.ts @@ -15,6 +15,7 @@ import { STATUS_OK, firebaseApi, } from "@mrgnlabs/marginfi-v2-ui-state"; +import posthog from "posthog-js"; initFirebaseIfNeeded(); @@ -72,6 +73,13 @@ export default async function handler(req: NextApiRequest, res: a } await logSignupAttempt(walletAddress, payload.uuid, "", true); + posthog.capture("user_login", { + publicKey: walletAddress, + uuid: payload.uuid, + }); + posthog.identify(payload.uuid, { + publicKey: walletAddress, + }); // Generate a custom token for the client to sign in const customToken = await admin.auth().createCustomToken(walletAddress); diff --git a/apps/marginfi-v2-ui/src/utils/mrgnActions.ts b/apps/marginfi-v2-ui/src/utils/mrgnActions.ts index b74a8e463c..43b1111e5f 100644 --- a/apps/marginfi-v2-ui/src/utils/mrgnActions.ts +++ b/apps/marginfi-v2-ui/src/utils/mrgnActions.ts @@ -4,6 +4,7 @@ import { isWholePosition } from "./mrgnUtils"; import { Connection, PublicKey, Transaction } from "@solana/web3.js"; import { Wallet, processTransaction } from "@mrgnlabs/mrgn-common"; import { WalletContextState } from "@solana/wallet-adapter-react"; +import posthog from "posthog-js"; import { WalletContextStateOverride } from "~/hooks/useWalletContext"; import { MultiStepToastHandle, showErrorToast } from "./toastUtils"; @@ -126,6 +127,11 @@ export async function deposit({ try { await marginfiAccount.deposit(amount, bank.address); multiStepToast.setSuccessAndNext(); + posthog.capture("user_deposit", { + amount, + bankAddress: bank.address.toBase58(), + tokenSymbol: bank.meta.tokenSymbol, + }); } catch (error: any) { const msg = extractErrorString(error); multiStepToast.setFailed(msg); @@ -152,6 +158,11 @@ export async function borrow({ try { await marginfiAccount.borrow(amount, bank.address); multiStepToast.setSuccessAndNext(); + posthog.capture("user_borrow", { + amount, + bankAddress: bank.address.toBase58(), + tokenSymbol: bank.meta.tokenSymbol, + }); } catch (error: any) { const msg = extractErrorString(error); multiStepToast.setFailed(msg); @@ -178,6 +189,11 @@ export async function withdraw({ try { await marginfiAccount.withdraw(amount, bank.address, bank.isActive && isWholePosition(bank, amount)); multiStepToast.setSuccessAndNext(); + posthog.capture("user_withdraw", { + amount, + bankAddress: bank.address.toBase58(), + tokenSymbol: bank.meta.tokenSymbol, + }); } catch (error: any) { const msg = extractErrorString(error); multiStepToast.setFailed(msg); @@ -204,6 +220,11 @@ export async function repay({ try { await marginfiAccount.repay(amount, bank.address, bank.isActive && isWholePosition(bank, amount)); multiStepToast.setSuccessAndNext(); + posthog.capture("user_repay", { + amount, + bankAddress: bank.address.toBase58(), + tokenSymbol: bank.meta.tokenSymbol, + }); } catch (error: any) { const msg = extractErrorString(error); multiStepToast.setFailed(msg); @@ -235,6 +256,9 @@ export async function collectRewardsBatch( tx.add(...ixs); await processTransaction(connection, wallet, tx); multiStepToast.setSuccessAndNext(); + posthog.capture("user_collect_rewards", { + bankAddresses, + }); } catch (error: any) { const msg = extractErrorString(error); multiStepToast.setFailed(msg); @@ -272,6 +296,11 @@ export const closeBalance = async ({ await marginfiAccount.repay(0, bank.address, true); } multiStepToast.setSuccessAndNext(); + posthog.capture("user_close_balance", { + positionType: bank.position.isLending ? "lending" : "borrow", + bankAddress: bank.address.toBase58(), + tokenSymbol: bank.meta.tokenSymbol, + }); } catch (error: any) { const msg = extractErrorString(error); multiStepToast.setFailed(msg); diff --git a/turbo.json b/turbo.json index 46a978b6cb..63a2a8a81e 100644 --- a/turbo.json +++ b/turbo.json @@ -59,6 +59,7 @@ "NEXT_PUBLIC_BIRDEYE_API_KEY", "NEXT_PUBLIC_WEB3_AUTH_CLIENT_ID", "NEXT_PUBLIC_MOONPAY_API_KEY", - "MOONPAY_SECRET_KEY" + "MOONPAY_SECRET_KEY", + "NEXT_PUBLIC_POSTHOG_API_KEY" ] } diff --git a/yarn.lock b/yarn.lock index 030fea63a9..2fba4df92b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14563,6 +14563,11 @@ fetch-retry@~5.0.3: resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.6.tgz#17d0bc90423405b7a88b74355bf364acd2a7fa56" integrity sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ== +fflate@^0.4.1: + version "0.4.8" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -20409,6 +20414,13 @@ postcss@^8.3.5, postcss@^8.4.20, postcss@^8.4.21, postcss@^8.4.23, postcss@~8.4. picocolors "^1.0.0" source-map-js "^1.0.2" +posthog-js@^1.93.3: + version "1.93.3" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.93.3.tgz#6216b761d57885b1d0f31459d13c40d40d724b0d" + integrity sha512-jEOWwaQpTRbqLPrDLY6eZr7t95h+LyXqN7Yq1/K6u3V0Y1C9xHtYhpuGzYamirVnCDTbVq22RM++OBUaIpp9Wg== + dependencies: + fflate "^0.4.1" + preact@10.4.1: version "10.4.1" resolved "https://registry.yarnpkg.com/preact/-/preact-10.4.1.tgz#9b3ba020547673a231c6cf16f0fbaef0e8863431"