Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"octokit": "4.1.2",
"parse-duration": "2.1.3",
"postcss": "8.5.3",
"posthog-js": "^1.249.5",
"posthog-node": "^4.18.0",
"pretty-format": "29.7.0",
"qrcode": "1.5.4",
"react": "19.1.0",
Expand Down Expand Up @@ -1639,6 +1641,8 @@

"copyfiles": ["[email protected]", "", { "dependencies": { "glob": "^7.0.5", "minimatch": "^3.0.3", "mkdirp": "^1.0.4", "noms": "0.0.0", "through2": "^2.0.1", "untildify": "^4.0.0", "yargs": "^16.1.0" }, "bin": { "copyfiles": "copyfiles", "copyup": "copyfiles" } }, "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg=="],

"core-js": ["[email protected]", "", {}, "sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA=="],

"core-util-is": ["[email protected]", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],

"cosmiconfig": ["[email protected]", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.1.0", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.7.2" } }, "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="],
Expand Down Expand Up @@ -2555,6 +2559,10 @@

"postcss-value-parser": ["[email protected]", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],

"posthog-js": ["[email protected]", "", { "dependencies": { "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", "web-vitals": "^4.2.4" }, "peerDependencies": { "@rrweb/types": "2.0.0-alpha.17", "rrweb-snapshot": "2.0.0-alpha.17" }, "optionalPeers": ["@rrweb/types", "rrweb-snapshot"] }, "sha512-ynB2bcSZz91xF36Aun2OgAvJ37WPjp8hrUHplZJTdJKhaX7j63CePR+Ved6NVO4YqwhCOVqepxwGGJXG4ROBeA=="],

"posthog-node": ["[email protected]", "", { "dependencies": { "axios": "^1.8.2" } }, "sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw=="],

"potpack": ["[email protected]", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="],

"preact": ["[email protected]", "", {}, "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w=="],
Expand Down Expand Up @@ -3019,6 +3027,8 @@

"wagmi": ["[email protected]", "", { "dependencies": { "@wagmi/connectors": "5.7.9", "@wagmi/core": "2.16.5", "use-sync-external-store": "1.4.0" }, "peerDependencies": { "@tanstack/react-query": ">=5.0.0", "react": ">=18", "typescript": ">=5.0.4", "viem": "2.x" }, "optionalPeers": ["typescript"] }, "sha512-CX+NpyTczVIST5DqLtasKZ3VrhImKQZ9XM9aDUVgOM46MRN/CykgGGAJfuIfpQ80LZ91GCY+JuitGknHUz7MNQ=="],

"web-vitals": ["[email protected]", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="],

"web3-eth-abi": ["[email protected]", "", { "dependencies": { "@ethersproject/abi": "5.0.7", "underscore": "1.12.1", "web3-utils": "1.3.6" } }, "sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ=="],

"web3-utils": ["[email protected]", "", { "dependencies": { "bn.js": "^4.11.9", "eth-lib": "0.2.8", "ethereum-bloom-filters": "^1.0.6", "ethjs-unit": "0.1.6", "number-to-bn": "1.7.0", "randombytes": "^2.1.0", "underscore": "1.12.1", "utf8": "3.0.0" } }, "sha512-hHatFaQpkQgjGVER17gNx8u1qMyaXFZtM0y0XLGH1bzsjMPlkMPLRcYOrZ00rOPfTEuYFOdrpGOqZXVmGrMZRg=="],
Expand Down Expand Up @@ -3645,6 +3655,8 @@

"parse-asn1/hash-base": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" } }, "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg=="],

"posthog-js/fflate": ["[email protected]", "", {}, "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="],

"prop-types/react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],

"psl/punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
Expand Down
4 changes: 4 additions & 0 deletions config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ const env = {
* By default, it is set to 30 minutes.
*/
minimumVotingPeriod: parseDuration(process.env.NEXT_PUBLIC_MINIMUM_VOTING_PERIOD, 1800),

// PostHog
posthogKey: process.env.NEXT_PUBLIC_POSTHOG_KEY ?? '',
posthogApiHost: process.env.NEXT_PUBLIC_POSTHOG_API_HOST ?? '',
};

export default env;
1 change: 1 addition & 0 deletions hooks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { default as useIsMobile } from './useIsMobile';
export * from './useDeviceDetect';
export * from './useLocalStorage';
export * from './useEstimateMaxAmount';
export * from './usePostHog';
120 changes: 120 additions & 0 deletions hooks/usePostHog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { useChain } from '@cosmos-kit/react';
import { usePostHog } from 'posthog-js/react';
import { useEffect, useRef } from 'react';

import env from '@/config/env';

interface TransactionEvent {
success: boolean;
transactionHash?: string;
chainId: string;
messageTypes: string[];
fee?: {
amount: string;
denom: string;
};
memo?: string;
error?: string;
gasUsed?: string;
gasWanted?: string;
height?: string;
}

export const useManifestPostHog = () => {
const posthog = usePostHog();
const { address, wallet, isWalletConnected } = useChain(env.chain);
const identifiedAddress = useRef<string | null>(null);
const lastWalletName = useRef<string | null>(null);

// Only identify user once per address or when wallet changes
useEffect(() => {
if (isWalletConnected && address && posthog) {
const walletName = wallet?.prettyName || null;

Check warning on line 32 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L32

Added line #L32 was not covered by tests

// Only identify if this is a new address or different wallet
if (identifiedAddress.current !== address || lastWalletName.current !== walletName) {
posthog.identify(address, {
wallet_address: address,
wallet_name: walletName,
wallet_mode: wallet?.mode,
chain_id: env.chainId,
chain_name: env.chain,
last_connected: new Date().toISOString(),
});

Check warning on line 43 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L35-L43

Added lines #L35 - L43 were not covered by tests

// Track wallet connection event
posthog.capture('wallet_connected', {
wallet_address: address,
wallet_name: walletName,
wallet_mode: wallet?.mode,
chain_id: env.chainId,
chain_name: env.chain,
});

Check warning on line 52 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L46-L52

Added lines #L46 - L52 were not covered by tests

// Update refs to track what we've identified
identifiedAddress.current = address;
lastWalletName.current = walletName;
}

Check warning on line 57 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L55-L57

Added lines #L55 - L57 were not covered by tests
}
}, [isWalletConnected, address, wallet, posthog]);

// Reset identification when wallet disconnects
useEffect(() => {
if (!isWalletConnected && posthog && identifiedAddress.current) {
// Track wallet disconnection event before resetting
posthog.capture('wallet_disconnected', {
chain_id: env.chainId,
chain_name: env.chain,
previous_address: identifiedAddress.current,
});

Check warning on line 69 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L65-L69

Added lines #L65 - L69 were not covered by tests

posthog.reset();
identifiedAddress.current = null;
lastWalletName.current = null;

Check warning on line 73 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L71-L73

Added lines #L71 - L73 were not covered by tests
}
}, [isWalletConnected, posthog]);

const trackTransaction = (event: TransactionEvent) => {
if (!posthog) return;

const eventName = event.success ? 'transaction_success' : 'transaction_failed';

posthog.capture(eventName, {
...event,
wallet_address: address,
wallet_name: wallet?.prettyName,
timestamp: new Date().toISOString(),
// Feature flags and cohorts can be automatically included
$groups: {
chain: env.chainId,
wallet_type: wallet?.prettyName || 'unknown',
},
});

// Update user properties with latest transaction info (minimal updates)
const updateProperties: Record<string, any> = {};

if (event.success) {
updateProperties.last_successful_transaction = new Date().toISOString();
if (event.transactionHash) {
updateProperties.last_transaction_hash = event.transactionHash;
}
} else {
updateProperties.last_failed_transaction = new Date().toISOString();
if (event.error) {
updateProperties.last_error = event.error;
}
}

// Only update if we have properties to set and user is identified
if (Object.keys(updateProperties).length > 0 && identifiedAddress.current === address) {
posthog.setPersonProperties(updateProperties);

Check warning on line 111 in hooks/usePostHog.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/usePostHog.tsx#L77-L111

Added lines #L77 - L111 were not covered by tests
}
};

return {
posthog,
trackTransaction,
isReady: !!posthog && isWalletConnected,
};
};
41 changes: 41 additions & 0 deletions hooks/useTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { useToast } from '@/contexts/toastContext';
import { Web3AuthContext } from '@/contexts/web3AuthContext';

import { useManifestPostHog } from './usePostHog';

interface Msg {
typeUrl: string;
value: any;
Expand Down Expand Up @@ -44,6 +46,7 @@
const { isSigning, setIsSigning, setPromptId } = useContext(Web3AuthContext);
const { address, getSigningStargateClient, estimateFee } = useChain(chainName);
const { setToastMessage } = useToast();
const { trackTransaction } = useManifestPostHog();
const explorerUrl = chainName === env.osmosisChain ? env.osmosisExplorerUrl : env.explorerUrl;

const tx = async (msgs: Msg[], options: TxOptions) => {
Expand Down Expand Up @@ -116,6 +119,24 @@
if (isDeliverTxSuccess(res)) {
if (options.onSuccess) options.onSuccess();

// Track successful transaction
trackTransaction({
success: true,
transactionHash: res.transactionHash,
chainId: chainName,
messageTypes: msgs.map(msg => msg.typeUrl),
fee: fee
? {
amount: fee.amount[0]?.amount || '0',
denom: fee.amount[0]?.denom || 'unknown',
}
: undefined,
memo: options.memo,
gasUsed: res.gasUsed?.toString(),
gasWanted: res.gasWanted?.toString(),
height: res.height?.toString(),
});

Check warning on line 139 in hooks/useTx.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/useTx.tsx#L122-L139

Added lines #L122 - L139 were not covered by tests
if (msgs.filter(msg => msg.typeUrl === '/cosmos.group.v1.MsgSubmitProposal').length > 0) {
const submitProposalEvent = res.events.find(
event => event.type === 'cosmos.group.v1.EventSubmitProposal'
Expand Down Expand Up @@ -143,6 +164,25 @@
}
return options.returnError ? { error: null } : undefined;
} else {
// Track failed transaction
trackTransaction({
success: false,
transactionHash: res.transactionHash,
chainId: chainName,
messageTypes: msgs.map(msg => msg.typeUrl),
fee: fee
? {
amount: fee.amount[0]?.amount || '0',
denom: fee.amount[0]?.denom || 'unknown',
}
: undefined,
memo: options.memo,
error: res?.rawLog || 'Unknown error',
gasUsed: res.gasUsed?.toString(),
gasWanted: res.gasWanted?.toString(),
height: res.height?.toString(),
});

Check warning on line 185 in hooks/useTx.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/useTx.tsx#L167-L185

Added lines #L167 - L185 were not covered by tests
if (options.showToastOnErrors !== false) {
setToastMessage({
type: 'alert-error',
Expand All @@ -156,6 +196,7 @@
} catch (e: any) {
console.error('Failed to broadcast or simulate: ', e);
const errorMessage = options.simulate ? extractSimulationErrorMessage(e.message) : e.message;

Check warning on line 199 in hooks/useTx.tsx

View check run for this annotation

Codecov / codecov/patch

hooks/useTx.tsx#L199

Added line #L199 was not covered by tests
if (options.showToastOnErrors !== false) {
setToastMessage({
type: 'alert-error',
Expand Down
18 changes: 18 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ const nextConfig = {
},
],
},
async rewrites() {
return [
{
source: '/ingest/static/:path*',
destination: 'https://us-assets.i.posthog.com/static/:path*',
},
{
source: '/ingest/:path*',
destination: 'https://us.i.posthog.com/:path*',
},
{
source: '/ingest/decide',
destination: 'https://us.i.posthog.com/decide',
},
];
},
// This is required to support PostHog trailing slash API requests
skipTrailingSlashRedirect: true,
};

module.exports = nextConfig;
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"octokit": "4.1.2",
"parse-duration": "2.1.3",
"postcss": "8.5.3",
"posthog-js": "^1.249.5",
"posthog-node": "^4.18.0",
"pretty-format": "29.7.0",
"qrcode": "1.5.4",
"react": "19.1.0",
Expand Down
40 changes: 31 additions & 9 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import '@fontsource/manrope';
import '@interchain-ui/react/styles';
import type { AppProps } from 'next/app';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
import { useEffect } from 'react';

import MobileNav from '@/components/react/mobileNav';
import { ManifestAppProviders } from '@/contexts/manifestAppProviders';
Expand All @@ -13,17 +16,36 @@ import '../styles/globals.css';
// TODO: remove asset list injections when chain registry is updated

function ManifestApp({ Component, pageProps }: AppProps) {
// Initialize PostHog on the client
useEffect(() => {
if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: '/ingest',
ui_host: 'https://us.posthog.com',
capture_pageview: 'history_change',
autocapture: false,
capture_exceptions: true, // enable capturing exceptions
loaded: ph => {
if (process.env.NODE_ENV === 'development') ph.debug();
},
debug: process.env.NODE_ENV === 'development',
});
}
}, []);

const [drawer, setDrawer] = useLocalStorage('isDrawerVisible', true);

return (
<ManifestAppProviders>
<AppContent
Component={Component}
pageProps={pageProps}
drawer={drawer}
setDrawer={setDrawer}
/>
</ManifestAppProviders>
<PostHogProvider client={posthog}>
<ManifestAppProviders>
<AppContent
Component={Component}
pageProps={pageProps}
drawer={drawer}
setDrawer={setDrawer}
/>
</ManifestAppProviders>
</PostHogProvider>
);
}

Expand Down Expand Up @@ -51,7 +73,7 @@ function AppContent({ Component, pageProps, drawer, setDrawer }: AppContentProps
</div>

<div
className={`flex-1 transition-all duration-300 ease-in-out
className={`flex-1 transition-all duration-300 ease-in-out \
ml-0 lg:ml-36 ${drawer ? 'lg:ml-[17rem]' : ''} relative z-0`}
>
<div className="lg:hidden pt-12">
Expand Down